After reading the Raku documentation, I only found this for undefining a variable. I believe that in Raku there are differences between assignment and binding.
Defining and undefining a scalar is easy.
> my $n
(Any)
> $n.defined
False
> $n = 3
3
> $n.defined
True
> $n = Nil
(Any)
> $n.defined
False
When the variable is binded, it's not possible.
> my $k := 5
5
> $k := Nil
===SORRY!=== Error while compiling:
Cannot use bind operator with this left-hand side
at line 2
------> <BOL>⏏<EOL>
> $k = Nil
Cannot assign to an immutable value
in block <unit> at <unknown file> line 1
For arrays or hashes, I can empty it, but the variable is still defined.
For functions, when you define a function with sub
, you cannot undefine it, but you can with an anonymous function.
> my &pera = -> $n { $n + 2}
-> $n { #`(Block|140640519305672) ... }
> &pera = Nil
(Callable)
> &pera.defined
False
> my &pera = -> $n { $n + 2}
-> $n { #`(Block|140640519305672) ... }
> &pera = Nil
(Callable)
> &pera.defined
False
> sub foo ($n) { $n + 1}
&foo
> &foo.defined
True
> &foo = Nil
Cannot modify an immutable Sub (&foo)
in block <unit> at <unknown file> line 1
So what's the difference between assignment and binding?
How can I undefine a variable?
Lots of different issues to discuss here.
> my $k := 5;
> $k := Nil;
Cannot use bind operator
This first problem is the Raku REPL. cf your last SO. Have you tried CommaIDE or repl.it?
Your code is perfectly valid:
my $k := 5;
$k := Nil;
say $k; # Nil
Moving on:
my $k := 5;
$k = Nil;
Cannot assign to an immutable value
This is different. After binding 5
to $k
, the $k = Nil
code is attempting to assign a value into a number. Only containers[1] support assignment. A number isn't a container, so you can't assign into it.
Clarifying some cases you mentioned but didn't explicitly cover:
my @foo;
@foo := Nil;
Type check failed in binding; expected Positional...
While scalar variables (ones with a $
sigil) will bind to any value or container, an @
sigil'd variable will only bind to a Positional
container such as an Array
. (Likewise a %
to an Associative
such as a Hash
.)
Not only that, but these containers are always defined. So they still return True
for .defined
even if they're empty:
my @foo := Array.new; # An empty array
say @foo.elems; # 0 -- zero elements
say @foo.defined; # True -- despite array being empty
Now assigning Nil
to an array:
my @foo;
@foo = Nil;
say @foo; # [(Any)]
If a declaration of an @
sigil'd variable doesn't bind it to some explicit Positional
type it is instead bound to the default choice for an @
variable. Which is an Array
with a default element value of Any
.
The @foo = Nil;
statement above assigns a Nil
value into the first element of @foo
. The assignment of a value into a non-existing element of a multi-element container means a new Scalar
container pops into existence and is bound to that missing element before assignment continues. And then, because we're assigning a Nil
, and because a Nil
denotes an absence of a value, the Array
's default value ((Any)
) is copied into the Scalar
instead of the Nil
.
On to the sub
case...
sub foo {}
&foo = {} # Cannot modify an immutable Sub (&foo)
&foo := {} # Cannot use bind operator ...
While a sub foo
declaration generates an &foo
identifier, it is deliberately neither assignable nor bindable. If you want a Callable
variable, you must declare one using ordinary variable declaration.
Unbind or undefine a variable
You can't unbind variables in the sense of leaving them bound to nothing at all. (In other words, Raku avoids the billion dollar mistake.) In some cases you can rebind variables.
In some cases you can bind or assign an undefined value to a variable. If it's not a scalar variable then that's like the @
variable example covered above. The scalar cases are considered next.
An example of the binding case:
my $foo := Any;
say $foo.defined; # False
say $foo; # (Any)
say $foo.VAR.WHAT; # (Any)
We'll see what the .VAR
is about in a moment.
The assignment case:
my $foo = Any;
say $foo.defined; # False
say $foo.WHAT; # (Any)
say $foo.VAR.WHAT; # (Scalar)
It's important to understand that in this case the $foo
is bound to a Scalar
, which is a container, which is most definitely "defined", for some definition of "defined", despite appearances to the contrary in the say $foo.defined;
line.
In the say $foo.WHAT;
line the Scalar
remains hidden. Instead we see an (Any)
. But the (Any)
is the type of the value held inside the Scalar
container bound to $foo
.
In the next line we've begun to pierce the veil by calling .VAR.WHAT
on $foo
. The .VAR
gets the Scalar
to reveal itself, rather than yielding the value it contains. And so we see the type Scalar
.
But if you call .defined
on that Scalar
it still insists on hiding!:
my $foo;
say $foo.VAR.defined; # False
say $foo.VAR.DEFINITE; # True
(The only way to force Raku to tell you the ultimate truth about its view of definiteness is to call the ultimate arbiter of definiteness, .DEFINITE
.)
The compiler will let you assign or bind a given new value or container to a variable, if doing so is valid according to the original variable declaration.
Assigning or binding an undefined value follows the same rules.
All variables must be bound by the end of their declaration.
If a variable's declaration allows an undefined value to be bound/assigned to that variable, then the variable can be undefined in that sense. But in all other circumstances and senses variables themselves can never be "unbound" or "undefined".
Binding is about making a variable correspond to some container or value:
Variables with @
and %
sigils must be bound to a container (default Array
and Hash
respectively).
Variables with the $
sigil must be bound to either a container (default Scalar
) or a value.
Variables with the &
sigil must be bound to a Callable
value or a Scalar
constrained to contain a Callable
value. (The &
sigil'd variable that's visible as a consequence of declaring a sub
does not allow rebinding or assignment.)
Assignment means copying a value into a container.
If a variable is bound to a container, then you can assign into it, provided the assignment is allowed by the compiler according to the original variable declaration.
If a variable is not bound to a container, then the compiler will reject an assignment to it.
If you use a scalar container as if it were a value, then you get the value that's held inside the container.
Binding a value (defined or undefined) to a scalar variable will mean it will stop acting as a container. If you then try to assign to that variable it won't work. You'd need to rebind it back to a container.
[1] In this answer I've used the word "container" to refer to any value that can serve as a container for containing other values. For example, instances of Array
, Hash
, or Scalar
.
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