Is there a purpose or benefit in prohibiting sigilless variables from rebinding? [closed]
Asked Answered
A

2

11

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?

Aphelion answered 17/5, 2018 at 20:9 Comment(2)
You may also have discovered, or if not, be surprised/delighted/dismayed/confused/enlightened by, my \a =$= 42; a = 99; and cousins (my \b =@= 1,2,3; b = 4,5,6 etc.)Sitton
@raiph: I hadn't realized it was possible to write \a = $ = 42; more compactly as my \a =$= 42;, but otherwise, yes, I had seen that you can bind/assign a sigilless symbol to an anoymous state (or even lexical) variable in order to get a container that can be assigned to repeatedly. In fact, I think that (and the documentation calling them sigilless variables) is part of what led to my original conception of sigilless symbols as variables.Aphelion
C
18

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

  • 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.

Chemotherapy answered 17/5, 2018 at 23:58 Comment(1)
Wow, amazingly clear and very illuminating answer. In light of your full response, your point about referring to sigilless symbols rather than sigilless variables is well taken.Aphelion
S
8

Sigilless variables have Static Single Assignment semantics.

Optimization opportunity benefits

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 of y. 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.

Human reasoning benefits

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.)

Sitton answered 17/5, 2018 at 20:39 Comment(2)
For human reasoning, don't constants take care of the "things that don't vary"?Tarmac
@raiph: Very helpful information! I didn't realize that binding a sigilless symbol to a Scalar container was a dubious practice, but I'm glad to hear it. I was previously confused and dismayed after all by the concept of a sigilless symbol that could never be rebound after initialization, but that could repeatedly be assigned new values by binding to a Scalar container. This all makes much more sense when thinking of sigilless symbols as, well, symbols rather than variables.Aphelion

© 2022 - 2024 — McMap. All rights reserved.