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 trait specifies that each of those arguments will be
evaluated in a scalar context.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 marginsThis 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.

