Sign In/My Account | View Cart  
advertisement


Listen Print Discuss

Exegesis 7
by Damian Conway | Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13

Editor's note: this document is out of date and remains here for historic interest. See Synopsis 7 for the current design information.

Why, how now, ho! From whence ariseth this?

Unlike sprintf and pack, the form subroutine isn't built into Perl 6. It's just a regular subroutine, defined in the Form.pm module:


    module Form
    {
        type FormArgs ::= Str|Array|Pair;

        sub form (FormArgs *@args is context(Scalar)) returns Str
            is exported
        {
            ...
        }

        ...
    }

That means that if we want to use form we need to be sure we:


    use Form;

first.

Note that the above definition of form specifies that the subroutine takes a list of arguments (*@args), each of which must be a string, array or pair (type FormArgs ::= Str|Num|Array|Pair). And the is context trait specifies that each of those arguments will be evaluated in a scalar context.

That last bit is important, because normally a "slurpy" array parameter like *@args would impose a list context on the corresponding arguments. We don't want that here, mainly because we're going to want to be able to pass arrays to form without having them flattened.


How called you...?

Like all Perl subroutines, form can be called in a variety of contexts.

When called in a scalar or list context, form returns a string containing the complete formatted text:


    my $formatted_text = form $format, *@data;

    @texts = ( form($format, *@data1), form($format, *@data2) );  # 2 elems

When called in a void context, form waxes lyrical about human frailty, betrayal of trust, and the pointlessness of calling out when nobody's there to heed the reply, before dying in a highly theatrical manner.


He doth fill fields...

The format strings passed to form determine what the resulting formatted text looks like. Each format consists of a series of field specifiers, which are usually separated by literal characters.

form understands a far larger number of field specifiers than format did, but they're easy to remember because they obey a small number of conventions:

  • Each field is enclosed in a pair of braces.
  • Within the braces, left or right angle brackets (< or >), bars (|), and single-quotes (') indicate various types of single-line fields.
  • Left or right square brackets ([ or ]), I's (I), and double- quotes (") indicate block fields of various types.
  • The direction of the brackets within a field indicates the direction towards which text will be justified in that field. For example:
    
        {<<<<<<<<<<<}   Justify the text to the left
        {>>>>>>>>>>>}                  Justify the text to the right
        {>>>>>><<<<<}                 Centre the text
        {<<<<<<>>>>>}   Fully  justify  the  text  to  both  margins

    This is even true for numeric fields, which look like: {>>>>>.<<}. The whole digits are right-justified before the dot and the decimals are left-justified after it.

  • An = at either end of a field (or both ends) indicates the data interpolated into the field is to be vertically "middled" within the resulting block. That is, the text is to be centred vertically on the middle of all the lines produced by the complete format.
  • An _ at the start and/or end of a field indicates the interpolated data is to be vertically "bottomed" within the resulting block. That is, the text is to be pushed to the bottom of the lines produced by the format.

The fields are fragrant...

That may still seem like quite a lot to remember, but the rules have been chosen so that the resulting fields are visually mnemonic. In other words, they're supposed to look like what they do. The intention is that we simply draw a (stylized) picture of how we want the finished text to look, using fields that look something like the finished product – left or right brackets brackets showing horizontal alignments, a middlish = or bottomed-out _ indicate middled or bottom vertical alignment, etc., etc. Then form fits our data into the fields so it looks right.

The typical field specifications used in a form format look like this:


                                      Field specifier
    Field type                 One-line             Block
    ==========                ==========          ==========

    left justified            {<<<<<<<<}          {[[[[[[[[}
    right justified           {>>>>>>>>}          {]]]]]]]]}
    centred                   {>>>><<<<}          {]]]][[[[}
    centred (alternative)     {||||||||}          {IIIIIIII}
    fully justified           {<<<<>>>>}          {[[[[]]]]}
    verbatim                  {''''''''}          {""""""""}

    numeric                   {>>>>>.<<}          {]]]]].[[}
    euronumeric               {>>>>>,<<}          {]]]]],[[}
    comma'd                   {>,>>>,>>>.<<}      {],]]],]]].[[}
    space'd                   {> >>> >>>.<<}      {] ]]] ]]].[[}
    eurocomma'd               {>.>>>.>>>,<<}      {].]]].]]],[[}
    Swiss Army comma'd        {>'>>>'>>>,<<}      {]']]]']]],[[}
    subcontinental            {>>,>>,>>>.<<}      {]],]],]]].[[}

    signed numeric            {->>>.<<<}          {-]]].[[[}
    post-signed numeric       {>>>>.<<-}          {]]]].[[-}
    paren-signed numeric      {(>>>.<<)}          {(]]].[[)}

    prefix currency           {$>>>.<<<}          {$]]].[[[}
    postfix currency          {>>>.<<<DM}         {]]].[[[DM}
    infix currency            {>>>$<< Esc}        {]]]$[[ Esc}

    left/middled              {=<<<<<<=}          {=[[[[[[=}
    right/middled             {=>>>>>>=}          {=]]]]]]=}
    infix currency/middled    {=>>$<< Esc}        {=]]$[[ Esc}
    eurocomma'd/middled       {>.>>>.>>>,<<=}     {].]]].]]],[[=}
    etc.

    left/bottomed             {_<<<<<<_}          {_[[[[[[_}
    right/bottomed            {_>>>>>>_}          {_]]]]]]_}
    etc.

What a block art thou...

When data is interpolated into a line field, the field grabs as much of the data as will fit on a single line, formats that data appropriately, and interpolates it into the format.

That means that if we use a one-line field, it only shows as much of the data as will fit on one line. For example:


    my $data1 = 'By the pricking of my thumbs, something wicked this way comes';
    my $data2 = 'A horse! A horse! My kingdom for a horse!';

    print form
        "...{<<<<<<<<<<<<<<<<<}...{>>>>>>>}...",
            $data1,               $data2;

prints:


    ...By the pricking of ... A horse!...

On the other hand, if our format string used block fields instead, the fields would extract one line of data at a time, repeating that process as many times as necessary to display all the available data. So:


    print form
        "...{[[[[[[[[[[[[[[[[[}...{]]]]]]]}...",
            $data1,               $data2;

would produce:


    ...By the pricking of ... A horse!...
    ...my thumbs,         ... A horse!...
    ...something wicked   ...       My...
    ...this way comes     ...  kingdom...
    ...                   ...    for a...
    ...                   ...   horse!...

We can mix line fields and block fields in the same format and form will extract and interpolate only as much data as each field requires. For example:


    print form
        "...{<<<<<<<<<<<<<<<<<}...{]]]]]]]}...",
            $data1,               $data2;

which produces:


    ...By the pricking of ... A horse!...
    ...                   ... A horse!...
    ...                   ...       My...
    ...                   ...  kingdom...
    ...                   ...    for a...
    ...                   ...   horse!...

Notice that, after the first line, the single-line {<<<<<<} field is simply replaced by the appropriate number of space characters, to keep the columns correctly aligned.

The usual reason for mixing line and block fields in this way is to allow numbered or bulleted points:


    print "I couldn't do my English Lit homework because...\n\n";

    for @reasons.kv -> $index, $reason {
        my $n = @reasons - $index ~ '.';
        print form "   {>}  {[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[}",
                       $n,  $reason,
                   "";
    }

which might produce:


    I couldn't do my English Lit homework because...

         10. Three witches told me I was going to be    
             king.

          9. I was busy explaining wherefore am I Romeo.

          8. I was busy scrubbing the blood off my      
             hands.

          7. Some dear friends had to charge once more  
             unto the breach.

          6. My so-called best friend tricked me into   
             killing my wife.

          5. My so-called best friend tricked me into   
             killing Caesar.

          4. My so-called best friend tricked me into   
             taming a shrew.

          3. My uncle killed my father and married my   
             mother.

          2. I fell in love with my manservant, who was
             actually the disguised twin sister of the
             man that my former love secretly married,
             having mistaken him for my manservant who
             was wooing her on my behalf whilst secretly
             in love with me.

          1. I was abducted by fairies.

Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13

Next Pagearrow