Quick Tip #11: Number, Strings, and NumberString Allomorphs

Perl 6 has a variety of ways to quote things (maybe too many), but they aren’t all the same thing. The angle-bracket quote words, <...> isn’t just qw// written with different characters. It tries to guess what the words inside the brackets mean. That wasn’t documented today, but Zoffix has changed the docs so now it does.

Start with qw//, the basic quotewords operators. It grabs groups of non-whitespace characters and produces a list of those strings:

$ perl6
> qw{ a b c}.perl
("a", "b", "c")
> qw{ a b c}.WHAT
(List)

This form doesn’t care what the strings are. Every item comes out a string:

> qw{ a b 137}.perl
("a", "b", "137")
> qw{ a b 137}.WHAT
(List)
> qw{ a b 137}.[*-1].perl
"137"
> qw{ a b 137}.[*-1].WHAT
(Str)

There’s also a more general quote operator, Q, that can take a :w adverb to do the same thing:

> Q:w{ a b 137}.perl
("a", "b", "137")

This is mostly what I expect. Everything in the quote words operators come out as the same sort of thing. But note, just because I expect something doesn’t mean it’s reality. You’ll be much happier with Perl 6 if you don’t expect it to act like some other language named Perl.

The angle bracket quote words

The angle bracket form, however, tries to guess what the strings mean. It uses val to turn strings that look like numbers into an allomorph. That’s something that can look different but retains its meaning. The idea of the digit 1 is the same as the string “1” and the string “one”, even if Perl 6 decides to represent them as different classes.

First, look at some values in the Perl 6 REPL. I use val on a bunch of strings and then call WHAT to see what val produced:

$ perl6
> val( "137" ).WHAT
(IntStr)
> val( "1e37" ).WHAT
(NumStr)
> val( "1/137" ).WHAT
(RatStr)
> val( "137+4i" ).WHAT
(ComplexStr)
> val( "abc" ).WHAT
(Str)

Previously, the quote words didn’t guess like this for us, but we can add the :v adverb (if you can find this in the docs, let me know so I can link to it):

$ perl6
> qw:v{ 137 1.37e2 137+0i 1/137 }.perl
(IntStr.new(137, "137"), NumStr.new(137e0, "1.37e2"), ComplexStr.new(<137+0i>, "137+0i"), RatStr.new(<1/137>, "1/137"))

This is what the angle-bracket form of word quoting does (“documented” in S02: Allomorphic value semantics:

> < 137 1.37e2 137+0i 1/137 >.perl
(IntStr.new(137, "137"), NumStr.new(137e0, "1.37e2"), ComplexStr.new(<137+0i>, "137+0i"), RatStr.new(<1/137>, "1/137"))

The val can tell you if Perl 6 thinks a string is a number and fail otherwise. Give it the :val-or-fail adverb and you might get an exception:

> val( "123Camelia", :val-or-fail )
Cannot convert string to number: trailing characters after number in '123⏏Camelia' (indicated by ⏏)

If you’re expecting the lax string to number conversion of Perl 5, you’re out of luck.

> "123Camelia" + 0
Cannot convert string to number: trailing characters after number in '123⏏Camelia' (indicated by ⏏)

val returns the starting string when it can’t convert it:

> my $s = val( "123Camelia" )
123Camelia
> $s.WHAT
(Str)

With :val-or-fail, you get a Failure object back instead:

> my $s = val( "123Camelia", :val-or-fail )
Cannot convert string to number: trailing characters after number in '123⏏Camelia' (indicated by ⏏)
> $s.WHAT
(Failure)

Digit strings that aren’t numbers

This means, though, that if you have strings that are digits but shouldn’t be numbers (i.e. strings that use digits to identify a thingy but for whom there is no algebra), then angle brackets aren’t what you want. You wouldn’t want to put postal codes in them, for instance. Or vehicle number plates. Those might be digits, but you wouldn’t add or multiply them. That is, you wouldn’t want to pass them
through val:

my @zip_codes =   < 02199 02902 01614 94039 >; # not what you want
my @zip_codes = qw{ 02199 02902 01614 94039 }; # stays as strings

If you’ve ever had to process postal code data that was incorrectly inserted, exported, or otherwise handled, you can appreciate this.

But, there are some times that you don’t know if you need the number or string form. Perl 6 has signatures that can let it decide which subroutine or method to use in multiple dispatch.

Indeed, it’s even difficult to justify putting most things into angle brackets because number things as strings already turn into the right thing with mathematical operators. You probably don’t need Perl 6 to guess at these things for you ahead of time.

The case for lists of allomorphs

So what is the case for the angle brackets? You want to use them when
you want numbers to be either strings or numbers types. Some
things in Perl 6 can constrain the types of values they will accept. A subroutine signature might call for an Int:

sub do_something ( Int $i ) { ... }

But, perhaps you’ve got a bunch of values of unknown origin or dubious parentage. The angle brackets leave strings as strings. It turns numbers and strings that look like numbers in allomorphs. The double angle brackets are for variable interpolation:

> my $a = '123';
123
> my $b = 'Butterfly'
Butterfly
> my $c = 321
321
> << $a $b $c >>.perl
(IntStr.new(123, "123"), "Butterfly", IntStr.new(321, "321"))

Now, consider the case of constructing an argument list. You want the compactness that comes with the implied commas and quotes, but you need number-like strings:

sub int-or-str ( Int $i, Str $s, Rat $r ) { say "Hello" }

int-or-str( 1, 'Butterfly', 1/2 );
int-or-str( |< 1 Butterfly 1/2 > );

In the first call, I make the argument list with commas and the types satisfy the signature for int-or-str. Everything works out.

If I use angle quotes (and turn the resulting List into an argument list (lowercase l) with the “turn that List into an argument list” operator (uppecase |)), it still works. The numberish things come out as IntStr or RatStr. Since RatStr is both Rat and Str under multiple inheritance, a RatStr satifies either.

The stronger case for single things

The single thingy angle brackets is useful mostly for grouping when you don’t a literal that may be constant folded (hence losing its type). Inside the angle brackets it’s a number type:

> <1/137>.WHAT
(Rat)

Wait a minute! That’s not a stringy thing at all! But watch this:

> < 1/137 >.WHAT
(RatStr)

Okay, that’s weird. With no spaces it’s just the number type. With some whitespace, it’s the allomorph type.

One comment

Leave a Reply

Your email address will not be published. Required fields are marked *

IntStr “137” Whole numbers
NumStr “1.37e2”
RatStr “1/137” Rational numbers
ComplexStr “137+0i” Complex numbers
Str “abc” Everything else