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.
Thou shalt have my best gown to make thee a pair...
Wait a minute...
Where exactly did we conjure that :locale syntax from?
And what, exactly, did it create? What is an "option"?
Well, we're passing :locale as an argument to form, and form's
signature guarantees us
that it can only accept a Str, or an Array, or a Pair as an
argument. So an "option" must be one of those three types, and that
funky :identifier syntax must be a constructor for the equivalent data
structure.
And indeed, that's the case. An "option" is just a pair, and the
funky :identifier syntax is just another way of writing a pair
constructor.
The standard "option" syntax is:
:key( "value" )
which is identical in effect to:
key => "value"
Both specify an autoquoted key; both associate that key with a value; both evaluate to a pair object that contains the key and value. So why have a second syntax for pairs?
Because it allows us to optimize the pair constructor syntax in two different ways. The now-familiar "fat arrow" pair constructor takes a key and a value, each of which can be of any type. In contrast, the key of an "option" pair constructor can only be an identifier, which is always autoquoted...at compile-time. So, if we use the "option" syntax we're guaranteed that the key of the resulting pair is a string, that the string that contains a valid identifier, and that the compiler can check that validity before the program starts.
Moreover, whereas the "fat arrow" has only one syntax, "options" have several highly useful syntactic variations. For example, "fat arrow" pairs can be especially annoying when we want to use them to pass named boolean arguments to a subroutine. For example:
duel( $person1, $person2, to_death=>1, no_quarter=>1, left_handed=>1, bonetti=>1, capoferro=>1 );
In contrast, "options" have a special default behaviour. If we leave off their
parenthesized value entirely, the implied value is 1. So we could rewrite
the preceding function call as:
duel( $person1, $person2, :to_death, :no_quarter, :left_handed, :bonetti, :capoferro );
Better still, when we have a series of options, we don't have to put commas between them:
duel( $person1, $person2, :to_death :no_quarter :left_handed :bonetti :capoferro );
That makes them even more concise and uncluttered, especially in
use statements:
use POSIX :errno_h :fcntl_h :time_h;
There are other handy "option" variants as well, all of which simply substitute the parentheses following their key for some other kind of bracket (and hence some other kind of value). The full list of "option"...err...options is:
Option syntax Is equivalent to
================== =============================
:key("some value") key => "some value"
:key key => 1
:key{ a=>1, b=>2 } key => { a=>1, b=>2 }
:key{ $^arg * 2; } key => { $^arg * 2; }
:key[ 1, 2, 3, 4 ] key => [ 1, 2, 3, 4 ]
:key«eat at Joe's» key => ["eat", "at", "Joe's"]
Despite the deliberate differences in conciseness and flexibility, we can use "options" and "fat arrows" interchangeably in almost every situation where we need to construct a pair (except, of course, where the key needs to be something other than an identifier string, in which case the "fat arrow" is the only alternative). To illustrate that interchangeability, we'll use the "option" syntax throughout most of the rest of this discussion, except where using a "fat arrow" is clearly preferable for code readability.
Meanwhile, back in the fields...
Some tender money to me...
Formatting numbers gets even trickier when those numbers represent money.
But form simply lets us specify how the local currency looks –
including leading, trailing, or infix currency markers; leading, trailing, or
circumfix negation markers; thousands separators; etc. – and then it
formats it that way. For example:
my @amounts = (0, 1, 1.2345, 1234.56, -1234.56, 1234567.89);
my %format = (
"Canadian (English)" => q/ {-$],]]],]]].[}/,
"Canadian (French)" => q/ {-] ]]] ]]],[ $}/,
"Dutch" => q/ {],]]],]]].[-EUR}/,
"German (pre-euro)" => q/ {-].]]].]]],[DM}/,
"Indian" => q/ {-]],]],]]].[ Rs}/,
"Norwegian" => q/ {kr -].]]].]]],[}/,
"Portuguese (pre-euro)" => q/ {-].]]].]]]$[ Esc}/,
"Swiss" => q/{Sfr -]']]]']]].[}/,
);
for %format.kv -> $nationality, $layout {
print form "$nationality:",
" $layout",
@amounts,
"\n";
}
produces:
Swiss:
Sfr 0.0
Sfr 1.0
Sfr 1.23
Sfr 1'234.56
Sfr -1'234.56
Sfr 1'234'567.89
Canadian (French):
0,0 $
1,0 $
1,23 $
1 234,56 $
-1 234,56 $
1 234 567,89 $
Dutch:
0.0EUR
1.0EUR
1.23EUR
1,234.56EUR
1,234.56-EUR
1,234,567.89EUR
Norwegian:
kr 0,0
kr 1,0
kr 1,23
kr 1.234,56
kr -1.234,56
kr 1.234.567,89
German (pre-euro):
0,0DM
1,0DM
1,23DM
1.234,56DM
-1.234,56DM
1.234.567,89DM
Indian:
0.0 Rs
1.0 Rs
1.23 Rs
1,234.56 Rs
-1,234.56 Rs
12,34,567.89 Rs
Portuguese (pre-euro):
0$0 Esc
1$0 Esc
1$23 Esc
1.234$56 Esc
-1.234$56 Esc
1.234.567$89 Esc
Canadian (English):
$0.0
$1.0
$1.23
$1,234.56
-$1,234.56
$1,234,567.89
Nice, eh?
Able verbatim to rehearse...
But sometimes too nice. Sometimes all we want is an existing block of data laid out into columns – without any fancy reformatting or rejustification. For example, suppose we have an interesting string like this:
$diagram = <<EODNA;
G==C
A==T
T=A
A=T
T==A
G===C
T==A
C=G
TA
AT
A=T
T==A
G===C
T==A
EODNA
and we'd like to put beside some other text. Because it's already carefully formatted, we really don't want to interpolate it into a left-justified field:
print form
'{[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]} {[[[[[[[[[[[[[[[}',
$diatribe, $diagram;
Because that would squash our lovely helix:
Men at some time are masters of their G==C
fates: / the fault, dear Brutus, is not in A==T
our genes, / but in ourselves, that we are T=A
underlings. / Brutus and Caesar: what A=T
should be in that 'Caesar'? / Why should T==A
that DNA be sequenced more than yours? / G===C
Extract them together, yours is as fair a T==A
genome; / transcribe them, it doth become C=G
mRNA as well; / recombine them, it is as TA
long; clone with 'em, / Brutus will start a AT
twin as soon as Caesar. / Now, in the names A=T
of all the gods at once, / upon what T==A
proteins doth our Caesar feed, / that he is G===C
grown so great? T==A
Nor would right-, full-, centre- or numeric- justification help in this instance. What we really need is "leave-it-the-hell-alone" justification – a field specifier that lays out the data exactly as it is, leading whitespace included.
And that's the purpose of a verbatim field. A verbatim single-line field
({'''''''''}) grabs the next line of data it's offered and inserts as
much of it as will fit in the field's width, preserving whitespace "as
is". Likewise a verbatim block field ({"""""""""}) grabs every line
of the data it's offered and interpolates it into the text without any
reformatting or justification.
And that's precisely what we needed for our diagram:
print form
'{[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]} {"""""""""""""""}',
$diatribe, $diagram;
to produce:
Men at some time are masters of their G==C
fates: / the fault, dear Brutus, is not in A==T
our genes, / but in ourselves, that we are T=A
underlings. / Brutus and Caesar: what A=T
should be in that 'Caesar'? / Why should T==A
that DNA be sequenced more than yours? / G===C
Extract them together, yours is as fair a T==A
genome; / transcribe them, it doth become C=G
mRNA as well; / recombine them, it is as TA
long; clone with 'em, / Brutus will start a AT
twin as soon as Caesar. / Now, in the names A=T
of all the gods at once, / upon what T==A
proteins doth our Caesar feed, / that he is G===C
grown so great? T==A
Note that, unlike other types of fields, verbatim fields don't break and wrap their data if that data doesn't fit on a single line. Instead, they truncate each line to the appropriate field width. So a too-short verbatim field:
print form
'{[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]} {""""""}',
$diatribe, $diagram;
results in gene slicing:
Men at some time are masters of their G==C
fates: / the fault, dear Brutus, is not in A==
our genes, / but in ourselves, that we are T
underlings. / Brutus and Caesar: what A
should be in that 'Caesar'? / Why should T==
that DNA be sequenced more than yours? / G===C
Extract them together, yours is as fair a T==A
genome; / transcribe them, it doth become C=G
mRNA as well; / recombine them, it is as TA
long; clone with 'em, / Brutus will start a AT
twin as soon as Caesar. / Now, in the names A=T
of all the gods at once, / upon what T==A
proteins doth our Caesar feed, / that he is G===
grown so great? T=
rather than teratogenesis:
Men at some time are masters of their G==C
fates: / the fault, dear Brutus, is not in A=-
our genes, / but in ourselves, that we are =T
underlings. / Brutus and Caesar: what -
should be in that 'Caesar'? / Why should T=A
that DNA be sequenced more than yours? / -
Extract them together, yours is as fair a A=T
genome; / transcribe them, it doth become T=-
mRNA as well; / recombine them, it is as =A
long; clone with 'em, / Brutus will start a G===C
twin as soon as Caesar. / Now, in the names T==A
of all the gods at once, / upon what C=G
proteins doth our Caesar feed, / that he is TA
grown so great? AT
A=T
T==A
G==-
=C
T-
==A

