I sometimes see code that (to me) uses the wrong sigil in front of the variable
my $arr = [1, 2, 3, 4, 5]; # an array
my $lst = (1, 2, 3, 4, 5); # a list
my $hash = {a => '1', b => '2'}; # a hash
my $func = -> $foo { say $foo }; # a callable
And it all just works exactly as expected
say $arr[0]; # 1
say $lst[1]; # 2
say $hash<a>; # 1
say $hash{'b'}; # 2
$func('hello'); # hello
Q1: What are the benefits of using the scalar container for this, rather than just using the 'correct' one?
I know that Perl only let collections store scalars, requiring things like multi-dimensional arrays be done via array references, with [...]
and {...}
being array and hash reference literals respectively.
To expand and clarify what I mean here, there's basically two ways to define things, by value and by reference:
# "values"
my @arr = (1, 2, 3, 4);
my %hash = (1 => 2, 3 => 4);
# which are accessed like this:
my $result1 = $arr[0];
my $result2 = $hash{1};
# references (note how the braces canged)
my $aref = [1, 2, 3, 4];
my $href = {1 => 2, 3 => 4};
# or making a reference to existing collections
my $aref2 = \@arr;
my $href2 = \%hash;
# which are accessed like this:
my $result3 = $aref->[0];
my $result4 = $href->{1};
The reasoning behind this madness is that Perl collections only really accept scalars, and references are just that. Using references is essentially a way to enable multidimensional arrays.
TL;DR, the distinction makes sense in Perl because they serve two distinctly different purposes.
TL;DR For computers, and humans, and therefore Raku too, a non-scalar (plural thing) is also a scalar (singular thing). (Whereas the converse may not be true.) For example, an
Array
is both a plural thing (an array of elements) and a single thing, anArray
. When you wish to syntactically and statically emphasize a datum's most generic singular nature, use$
.Here's an opening example based on @sid_com++'s comment:
The first loop binds values to
$b
. It is "fault tolerant" because it accepts any value. The second loop binds to@b
. Any value that doesn't do thePositional
role leads to a type check failure.My Raku equivalent of your Perl code
Here's a Raku translation of your Perl code:
The code is a little shorter. Idiomatic code would probably be a good bit shorter still, dropping:
The
ref
variables. A variable@foo
is a reference. A[...]
in term (noun) position is anArray
reference literal. There's little or no need to use scalar variables to explicitly store references.The parens in the first couple lines;
Semi colons after most closing braces that are the last code on a line;
Raku's sigils are invariant. Here are two tables providing an at-a-glance comparison of Perl's sigil variation vs Raku's sigil invariance.
Why bother with sigils?
All the sigil variations directly correspond to embedding "type" info into an identifier's name that's visible to humans, the language, and the compiler:
foo Tells Raku features which pick between a singular and plural way of operating on data should decide based on the run-time type of the data.
$foo
Tells Raku to pick singular behavior. A value might be, say, aList
containing many values, but its singular nature is being emphasized instead.&foo
Type checks that a bound or assigned value does theCallable
role.@foo
Tells Raku to pickIterable
behavior. Also type checks that bound values do thePositional
role. AList
orArray
can be bound, but trying to bind to42
or aHash
will yield a type error.%foo
Tells Raku to pickIterable
behavior. Also type checks that bound values do theAssociative
role. APair
orBag
can be bound, but trying to bind to42
or aList
will yield a type error.I'll next consider your question for each sigil alternative.
Slashing out sigils
Repeating your examples, but this time "slashing out" sigils:
These almost just work exactly as expected:
See the
$ vs &
below for why it'sfunc.(...)
not justfunc(...)
. This last nosigil case is of little consequence because in Raku one normally writes:Identifiers with their sigils slashed out are SSA form. That is to say, they are permanently bound, once, at compile-time, to their data. Value type data is immutable. A reference is also immutable (although the data it refers to can change), so for example, if it's an array, it will remain the same array.
(See Is there a purpose or benefit in prohibiting sigilless variables from rebinding? for further discussion.)
$foo
instead of@foo
?Raku supports:
Lazy lists. (This can be very useful.)
A boolean
.is-lazy
method that indicates whether list assignment (@foo = ...
) should treat an assigned object as lazy or eager. Importantly, a lazy list is allowed to returnFalse
. (This too can be very useful.)Infinite lazy lists. (Yet another thing that can be very useful.)
The above three features are individually useful, and can be useful together. But while it is appropriate that Raku doesn't try to police these features other than the way it does, one needs to follow rules to avoid problems. And the simplest way to do that is to use the right sigil when it matters, as explained next.
Let's say
infinite
is an infinite lazy list that returnsFalse
for.is-lazy
:The first two lines work fine. The third hangs, trying to copy an infinite number of elements into
@foo
.Is it one thing or many things? Of course, if it's a list, it's both:
The choice of sigil in the above just indicates what view you want language constructs and readers to take by default. You can reverse yourself if you wish:
Assignment is different:
Assignment to an
@
variable "slurps" all remaining arguments. (Binding with:=
and metaops likeZ=
invoke scalar semantics, i.e. don't slurp.)We see another difference here; assigning to a
$
variable is going to keep aList
aList
, but assigning to an@
variable "slurps" its values into whatever container the@
variable is bound to (by default, anArray
).A tiny thing is string interpolation:
$foo
instead of%foo
?Again, is it one thing or many things? If it's a hash, it's both.
Assignment and string interpolation are also different in a manner analogous to
@
.$foo
instead of&foo
?This section is just for completeness. It only shows one reason to use
$
. And I've just made it up for this answer -- I don't recall seeing anyone using it.As with the other sigil alternatives, the primary difference would be whether you do or don't want to emphasize the
Callable
nature of a callable.As the setup, note that a
sub
declaration in Raku declares a corresponding constant identifier with an&
sigil:Which means that if you declare a mutable routine variable with the
&
sigil you can call it without the sigil:If you wanted to declare a mutable routine variable and not allow it to be easily called without a sigil you could declare it with
$
instead:Btw, this is why you get:
Reference literals
Despite your examples, knowing Perl (at least I did last century), and pondering what you've written, I'm still unclear what you're asking.
A wide range of programming languages adopt
[...]
in term (noun) position as a reference to a literal array. There are other common conventions for other data structure literals. This is what Raku does.Thus one can write:
Is that the sort of thing you're talking about?
Dereference literals
postcircumfix:< [ ] >
is declared as a pile of multi subs that (are supposed to) apply aPositional
consistent indexing protocol on their left argument.All built in types that do the
Positional
role work.User defined types that do the
Positional
role should work because the role defines typed interface stubs that must be implemented by types that do the role.But ducktyping is also OK; provided a type implements the basics of the interface
postcircumfix:< [ ] >
it should work.The same story applies for
postcircumfix:< { } >
andpostcircumfix:« < > »
, but the relevant role/protocol isAssociative
consistent indexing.And a similar story applies for
postcircumfix:< ( ) >
andCallable
.