In trying to better understand sigilless variables and how they differ from $
sigiled variables, I discovered that, unlike $
sigiled variables, sigilless variables cannot be rebound after they've been initialized:
my $a = 42;
my $b := $a;
$b := 42; # No exception generated
my \c := $a;
c := 42; # OUTPUT: «Cannot use bind operator with this left-hand side»
Is this by design? If so, is there a purpose or benefit to prohibiting sigilless variables from rebinding when $
sigiled variables are not prohibited from doing so?
Yes, it's certainly by design, and - like most things in Perl (6) design - it's this way for more than one reason.
Before discussing the sigilless symbol syntax, it's worth taking a moment to recall some of the roles sigils play in the language. These include:
my @a = @b
means iterate @b
and put each thing in it into @a
, thus any future assignments to @b
will not affect @a
)Positional
things to @
, for example)$
, controlling what will be considered a single item@
on a signature parameter, causing an incoming Seq
to be cachedEach sigil, therefore, carries a set of useful default behaviors for data that is to be considered a single item ($
), something to index in to positionally (@
), something to index in to with a key (%
), and something that can be called (&
). Each of these, both in the context of assignment, low-level binding, and signature binding, carry generally desirable semantics for that kind of data.
This is fine and well until one wants to write code that is polymorphic over all of these behaviors, or to not commit to any of the sigil behaviors. Earlier iterations of the Perl 6 language design had something like sub foo($x is parcel) { }
, where the is parcel
thing effectively meant "don't impose any kind of sigil-y semantics on this", except that was rather confusing, because the thing had the $
sigil but was opted out of the semantics. It was realized that if the sigil behaviors weren't to apply, then it'd be rather better if it looked different (the "different things should look different" design principle, which also shows up repeatedly in Perl). The most obvious way to look different was...to not have the sigil.
However, for syntactic reasons, something was needed in the signature to disamgbiguate the name being introduced from a type (since a signature like (Foo)
to match on the type Foo
but ignore the value was already supported, and useful, and we didn't want to lose that). The \
was picked to play that role, getting sub foo(\x) { }
, which then allowed the use of x
inside of the subroutine.
My recollection is that allowing this form in the case of my
came a bit later on, though I'm not entirely sure about that. One of the important things about a sigilless symbol not committing to a behavior is that it also doesn't commit to an assignment behavior, thus an =
on it is a little more late-bound (where possible the compiler considers the sigil, and emits quite different code for $
/&
and @
/%
assignments). Of course, if the symbol is bound to a value, then no assignment is possible.
That leaves the question of binding behavior. It was decided to make the sigilless symbol form a "static single assignment" syntax, as explained in one of the other answers. There were various reasons for this, including:
constant
s without a sigil.Finally, I'll note that I find the term "sigilless variable" a bit misleading, because there's nothing variable about it whatsoever. It's a syntax for introducing a symbol that is initialized bound to a particular thing and always will be for the (lexical) lifetime of that symbol. The best way of thinking about them is probably to consider them distinct from variables - which imply storage - and instead just consider them a way to attach a name to a value.
Sigilless variables have Static Single Assignment semantics.
Quoting from the Benefits section of the above linked SSA page:
The primary usefulness of SSA comes from how it simultaneously simplifies and improves the results of a variety of compiler optimizations, by simplifying the properties of variables. For example, consider this piece of code:
y := 1
y := 2
x := y
Humans can see that the first assignment is not necessary, and that the value of
y
being used in the third line comes from the second assignment ofy
. A program would have to perform reaching definition analysis to determine this. But if the program is in SSA form, both of these are immediate:
y1 := 1
y2 := 2
x1 := y2
Compiler optimization algorithms which are either enabled or strongly enhanced by the use of SSA include [a long list of available optimizations].
So, in principle, by writing code using sigilless variables you provide more opportunities to the compiler for optimization.
In general, in P6, things that vary have a sigil, and vice-versa, and things that don't, don't, and vice-versa. From this perspective it makes sense to disallow rebinding.
(For this reason I tend to think that binding a sigilless variable to a container, as I showed in my comment on your question, especially binding to a Scalar
container, is a dubious practice.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With