Not So Obvious Semantic Changes (Part 3 of 3)
Elizabeth Mattijsen
Posted on August 10, 2023
No Return Context in Raku
In Perl, a subroutine can determine in what context it is being called by using the wantarray
function. Available contexts are scalar
, list
and void
:
# Perl
sub say_context {
say wantarray
? "list"
: defined(wantarray)
? "scalar"
: "void";
}
my $a = say_context(); # scalar
my @b = say_context(); # list
say_context(); # void
In Raku, there is no such thing a caller's context. A subroutine (well, technically any block) only ever returns a single value. Such a value may well be a List
, or any other object.
So you can simulate returning multiple values in Raku:
# Raku
sub return-scalar() { 42 }
sub return-list() { 42,666 } # note the comma makes the list
my $a = return-scalar;
say $a; # 42
my ($b,$c) = return-list; # unpack list into variables
say $b; # 42
say $c; # 666
But you should realise that it is in fact a single List
that is being returned by return-list
, that will be unpacked into $b
and $c
.
Simulating void
context is possible in a way in Raku, but it requires returning an object that has a specific sink
method defined for it. And that goes beyond the scope of these blog posts.
Just Saying
In Perl, using the print
command will stringify the given value and send this to standard output. The say
command will add a newline after that.
In Raku, there are basically 3 methods with which an object can be stringified: Str
, gist
and raku
. The Str
method is supposed to return a complete stringification of the given expression. The gist
method is intended to provide a gist of the stringification of the given expression. For small objects, this is usually the same as the Str
method. For bigger objects, the gist
method is at liberty to drop information, or to summarise it.
The raku
method provides basically a built-in Data::Dumper
: the method is supposed to return a string that can be evaluated to re-create the given object. This is of course not always 100% possible, but all effort should be taken to make it roundtrip.
So given these methods, how do these relate to outputting strings? Well, the print
command in Raku basically works like the print
command in Perl: it calls the Str
method on the given expression and sends that to standard output.
The say
command in Perl merely adds a newline to the stringification of the expression:
# Perl
say $frobnicated; # stringify and send to STDOUT with newline
In Raku, the say
command first calls the gist
method (rather than the Str
method), and adds a newline.
# Raku
say $frobnicated; # call method .gist and send to STDOUT with newline
In Raku, if you want to have the standard stringification happen when outputting to standard output, you can either call the Str
method on the expression explicitly, or you can prefix the expression with a ~
, or you can use the put
command:
# Raku
say $frobnicated.Str; # explicit standard stringification
say ~$frobnicated; # prefix ~ is same as .Str
put $frobnicated; # implicit standard stringification
Additionally, Raku has a note
command, that functions the same as say
, but will send the output to standard error:
# Raku
note $frobnicated; # call method .gist and send to STDERR with newline
Which is similar to:
# Perl
say STDERR $frobnicated;
except that method gist
is called rather than the standard stringification.
The Rakudo implementation of the Raku Programming Language also includes a tiny Data::Dumper
facility, so tiny it lost the t: dd
. This basically calls the raku
method on the given expression, and prints that, with possibly some extra information, on standard error:
# Raku
dd "foo"; # "foo"
dd 5+42i; # <5+42i>
dd now; # Instant.from-posix(1691224400.111166001)
dd Date.today; # Date.new(2023,8,5)
my Int $a = 42;
dd $a; # Int $a = 42
Note that this feature is purely intended to facilitate debugging, and should probably not be used in any code running in production.
Always Expression between Curlies
In Raku, something between curly braces ({ }
) is always an expression, no matter where it occurs. This applies to indexing into a hash, where this differs in semantics from Perl:
# Perl
%hash{a} = 42;
# the bareword "a" interpreted as a string
Whereas in Raku, everything between curly braces is an expression, so:
# Raku
%hash{a} = 42;
# bareword "a" interpreted as a call to subroutine "a"
Fortunately, if that subroutine does not exist in that lexical scope, there will be a compilation error that will be very clear:
# Raku
%hash{a} = 42;
# ===SORRY!=== Error while compiling …
Undeclared routine: a
used at line …
For all other normal code uses (if
, for
, while
, etc.), the curly braces act the same in Perl and Raku.
However in Raku, they can also be used inside double quoted strings:
# Raku
my $a = 42;
my $b = 666;
say "The sum of $a and $b is { $a + $b }";
# The sum of 42 and 666 is 708
Which is a lot more consistent and a lot less line-noisy than the equivalent in Perl using @{[ ]}
:
# Perl
my $a = 42;
my $b = 666;
say "The sum of $a and $b is @{[ $a + $b ]}";
# The sum of 42 and 666 is 708
Summary
This blog post elaborated on the absence of context in Raku, the subtle semantic differences between say
and friends, and the fact that the contents of curlies are always an expression in Raku.
Posted on August 10, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.