Is there a purpose or benefit in prohibiting sigilless variables from rebinding?

  • A+

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:

  • Syntactic disambiguation (you can call a variable whatever you want, even if there happens to be a keyword with that name)
  • Readability (the data in the program stands out thanks to the sigil)
  • Defining assignment semantics (in Perl 6 assignment means "copy in to", thus my @a = @b means iterate @b and put each thing in it into @a, thus any future assignments to @b will not affect @a)
  • Restricting what can be bound there (only Positional things to @, for example)
  • In the case of the $, controlling what will be considered a single item
  • In the case of @ on a signature parameter, causing an incoming Seq to be cached

Each 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 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:

  • Helping to preserve the "data stands out" readability property of sigils, by at least making sure that anything that is to be (immediately) mutable still does carry a sigil
  • Also enhancing program readability by having a form that lets the reader know that the symbol will never be rebound to a new value, so one can effectively consider it constant within the scope. This is consistent with it being easy to declare real constants without a sigil.
  • Gently encouraging a more functional style (perhaps by making those who decide they hate sigils pay the rest of us back with code that's at least more readable in a different way than the sigils offer :-))

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.


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: