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.

Lay out. Lay out.

As we saw earlier, with follow-on fields and overflow fields, form is perfectly happy to have several fields in a single format that are all fed by the same data source. For example:


    print form
        "{[[[[[[[[]]]]]]]]]]…}   {…[[[[[[[]]]]]]]]]]…}   {…[[[[[[[[]]]]]]]]]]}",
             $soliloquy,             $soliloquy,              $soliloquy;

In fact, that kind of format is particularly useful for creating multi-column outputs (like newspaper columns, for example).

But a small quandry arises. In what order should form fill in these fields? Should the data be formatted down the page, filling each column completely before starting the next (and therefore potentially leaving the last column "short"):


    Now is the winter  of   torious  wreaths;   /   front; / And now, in-
    our discontent / Made   Our bruised arms hung   stead of mounting ba-
    glorious  summer   by   up for  monuments;  /   rded steeds / To fri-
    this sun of  York;  /   Our stern alarums ch-   ght the souls of fea-
    And  all  the  clouds   anged to merry meeti-   rful  adversaries,  /
    that lour'd upon  our   ngs, /  Our  dreadful   He capers nimbly in a
    house / In  the  deep   marches to delightful   lady's chamber.
    bosom  of  the  ocean   measures.   /   Grim-   
    buried. / Now are our   visaged war hath smo-   
    brows bound with vic-   oth'd  his   wrinkled

Or should the data be run line-by-line across all three columns (the way a Perl 5 format does it), filling one line completely before starting the next:


    Now is the winter  of   our discontent / Made   glorious  summer   by
    this sun of  York;  /   And  all  the  clouds   that lour'd upon  our
    house / In  the  deep   bosom  of  the  ocean   buried. / Now are our
    brows bound with vic-   torious  wreaths;   /   Our bruised arms hung
    up for  monuments;  /   Our stern alarums ch-   anged to merry meeti-
    ngs, /  Our  dreadful   marches to delightful   measures.   /   Grim-
    visaged war hath smo-   oth'd  his   wrinkled   front; / And now, in-
    stead of mounting ba-   rded steeds / To fri-   ght the souls of fea-
    rful  adversaries,  /   He capers nimbly in a   lady's chamber.

Or should the text run down the columns, but in such a way as to leave those columns as evenly balanced in length as possible:


    Now is the winter  of   brows bound with vic-   visaged war hath smo-
    our discontent / Made   torious  wreaths;   /   oth'd  his   wrinkled
    glorious  summer   by   Our bruised arms hung   front; / And now, in-
    this sun of  York;  /   up for  monuments;  /   stead of mounting ba-
    And  all  the  clouds   Our stern alarums ch-   rded steeds / To fri-
    that lour'd upon  our   anged to merry meeti-   ght the souls of fea-
    house / In  the  deep   ngs, /  Our  dreadful   rful  adversaries,  /
    bosom  of  the  ocean   marches to delightful   He capers nimbly in a
    buried. / Now are our   measures.   /   Grim-   lady's chamber.

Well, of course, there's no "right" answer to that; it depends entirely on what kind of effect we're trying to achieve.

The first approach (i.e. lay out the text down each column first) works well if we're formatting a news-column, or a report, or a description of some kind. The second (i.e. lay out the text across each line first), is excellent for putting diagrams or call-outs in the middle of a piece of text (as we did for Mrs Miggins). The third approach (i.e. lay out the data downwards but balance the columns) is best for presenting a single list of data in multiple columns – like ls does.

So we need an option with which to tell form which of these useful alternatives we want for a particular format. That option is named :layout and can take one of three string values: "down", "across", or "balanced". So, for example, to produce three versions of Richard III's famous monologue in the order shown above, we'd use:


    print form :layout«down»,
        "{[[[[[[[[]]]]]]]]]]…}   {…[[[[[[[]]]]]]]]]]…}   {…[[[[[[[[]]]]]]]]]]}",
             $soliloquy,             $soliloquy,              $soliloquy;

then:


    print form :layout«across»,
        "{[[[[[[[[]]]]]]]]]]…}   {…[[[[[[[]]]]]]]]]]…}   {…[[[[[[[[]]]]]]]]]]}",
             $soliloquy,             $soliloquy,              $soliloquy;

then:


    print form :layout«balanced»,
        "{[[[[[[[[]]]]]]]]]]…}   {…[[[[[[[]]]]]]]]]]…}   {…[[[[[[[[]]]]]]]]]]}",
             $soliloquy,             $soliloquy,              $soliloquy;

By the way, the default value for the :layout option is "balanced" since formatting regular columns of data is more common than formatting news or advertising inserts.

For the table, sir, it shall be served...

The :layout option controls one other form of inter-column formatting: tabular layout.

So far, all the examples of tables we've created (for example, our normalized scores) lined up nicely. But that was only because each item in each row happened to take the same number of lines (typically just one). So, a table generator like this:


    my @play = map {"$_\r"}  ( "Othello", "Richard III", "Hamlet"   );
    my @name = map {"$_\r"}  ( "Iago",    "Henry",       "Claudius" );

    print form 
         "Character       Appears in  ",
         "____________    ____________",
         "{[[[[[[[[[[}    {[[[[[[[[[[}",
          @name,          @play;

correctly produces:


    Character       Appears in
    ____________    ____________
    Iago            Othello     
                              
    Henry           Richard III

    Claudius        Hamlet

Note that we appended "\r" to each element to add an extra newline after each entry in the table. We can't use "\n" to specify a line-break within an array element, because form uses "\n" as an "end-of-element" marker. So, to allow line breaks within a single element of an array datum, form treats "\r" as "end-of-line-but-not-end-of-element" (somewhat like Perl 5's format does).

However, if we were to use the full titles for each character and each play:


    my @play = map {"$_\r"}  ( "Othello, The Moor of Venice",
                               "The Life and Death of King Richard III",
                               "Hamlet, Prince of Denmark",
                             );

    my @name = map {"$_\r"}  ( "Iago",
                               "Henry,\rEarl of Richmond",
                               "Claudius,\rKing of Denmark",
                             );

the same formatter would produce:


    Character       Appears in
    ____________    ____________
    Iago            Othello, The
                    Moor of     
    Henry,          Venice      
    Earl of
    Richmond        The Life and         
                    Death of    
    Claudius,       King Richard
    King of         III         
    Denmark         
                    Hamlet,     
                    Prince of   
                    Denmark

The problem is that the two block fields we're using just grab all the data from each array and format it independently into each column. Usually that's fine because the columns are independent (as we've previously seen).

But in a table, the data in each column specifically relates to data in other columns, so corresponding elements from the column's data arrays ought to remain vertically aligned. To achieve this, we simply tell form that the data in the various columns should be laid out like a table:


    print form :layout«tabular»,
         "Character       Appears in  ",
         "____________    ____________",
         "{[[[[[[[[[[}    {[[[[[[[[[[}",
          @name,          @play;

which then produces the desired result:


    Character       Appears in
    ____________    ____________
    Iago            Othello, The
                    Moor of     
                    Venice      
                                
    Henry,          The Life and
    Earl of         Death of    
    Richmond        King Richard
                    III         
                                
    Claudius,       Hamlet,     
    King of         Prince of   
    Denmark         Denmark

Give him line and scope...

Sometimes we want to use a particular option or combination of options in every call we make to form. Or, more likely, in every call we make within a specific scope. For example, we might wish to default to a different line-breaking algorithm everywhere, or we might want to make repeated use of a new type of field specifier, or we might want to reset the standard page length from a printable 60 to a screenable 24.

Normally in Perl 6, if we wanted to preset a particular optional argument we'd simply make an assumption:


    my &down_form := &form.assuming(:layout«down»);

But, of course, form collects all of its arguments in a single slurpy array, so it doesn't actually have a $layout parameter that we can prebind.

Fortunately, the .assuming method is smart enough to recognize when it being applied to a subroutine whose arguments are slurped. In such cases, it just prepends any prebound arguments to the resulting subroutine's argument list. That is, the binding of down_form shown above is equivalent to:


    my &down_form :=
        sub (FormArgs *@args is context(Scalar)) returns Str {
            return form( :layout«down», *@args );
        };

This was your default...

form provides one other mechanism by which options can be prebound. To use it, we (re-)load the Form module with an explicit argument list:


    use Form :layout«down», :locale, :interleave;

This causes the module to export a modified version of form in which the specified options are prebound. That modified version of form is exported lexically, and so form only has the specified defaults preset for the scope in which the use Form statement appears.

These default options are handy if we have a series of calls to form that all need some consistent non-standard behaviour. For example:


    use Form :layout«across»,
             :interleave,
             :page{ :header("Draft $(localtime)\n\n") };

    print form $introduction_format, *@introduction_data;

    for @sections -> $format, @data {
        print form $format, *@data;
    }

    print form $conclusion_format, *@conclusion_data;

Another use is to set up a fixed formatting string into which different data is to be interpolated (much in the way Perl 5 formats are typically used). For example, we might want a standard format for errors in a CATCH block:


    CATCH {
        use Form :interleave, <<EOFORMAT;
                     Error {<<<<<<<}: {[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[}
                     ___________________________________________________
                     EOFORMAT

        when /Missing datum/ { warn form "EMISSDAT", $_.msg }
        when /too large/     { warn form "ETOOBIG",  $_.msg }
        when .core           { warn form "EINTERN",  "Internal error" }
        default              { warn form "EUNKNOWN", "Seek help" }
    }

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

Next Pagearrow