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" }
}

