Perl5 -> Perl 6: everything is an object!
What does it mean
everything is an object?
Coming from other
OOP languages this statement could be clear, but not all the languages adhere to it strictly.
In Perl 6
everything is an object means that even not assigned values are objects!
Nil
is always the same
First of all, Perl 6 declares the special class
[Nil](https://docs.perl6.org/type/Nil)
that is a placeholder for
null values.
With two particular exceptions:
- a
Nil
value is an object (so you can call methods on it);
- a
Nil
object is always equal to another Nil
object (i.e., there is only one version of being null!).
A not initialized value does not get a Nil
value for free, the type Nil
can be explicity used by a developer to indicate the variable still does not contain any particular value.
Any
is all
The special type
[Any](https://docs.perl6.org/type/Any)
is the de-facto base class for everything in Perl 6. Well, the special class
[Mu](https://docs.perl6.org/type/Mu)
is the
root of the class hierarchy in Perl 6, but as the documentation states:
Note that most classes do not derive from Mu directly, but rather from Any.
that means that
Any
is the thing used as concrete root for the classes in Perl 6.
Any unassigned value in Perl 6 got assigned to Any
.
Introspection
Perl 6 provides special methods to introspect a variable, that is a class instance, that is in turn an object.
Let’s start simple:
"\nDumping a string scalar".say;
my $var = 'Hello World';
say 'NAME -> ' ~ $var.^name;
say 'PERL -> ' ~ $var.perl;
say 'WHO -> ' ~ $var.WHO;
say 'VAR -> ' ~ $var.VAR;
say 'WHICH -> '~ $var.WHICH;
"\nDumping a list".say;
my @list = ( 1, 2, 3, 4);
say 'NAME -> ' ~ @list.^name;
say 'PERL -> ' ~ @list.perl;
say 'WHO -> ' ~ @list.WHO;
say 'VAR -> ' ~ @list.VAR;
say 'WHICH -> '~ @list.WHICH;
will produce the output:
Dumping a string scalar
NAME -> Str
PERL -> "Hello World"
WHO -> Str
VAR -> Hello World
WHICH -> Str|Hello World
Dumping a list
NAME -> Array
PERL -> [1, 2, 3, 4]
WHO -> Array
VAR -> 1 2 3 4
WHICH -> Array|94409967984048
As you can see, the scalar variable is of type
Str
and the array is not surprisingly of type
Array
. The
perl
method provides a common way to get a Perl 6 representation of the instance value, so for example the string or the list.
What about an empty value? As already stated, a not initialized value got
Any
:
"\nDumping an empty value".say;
my @elist;
say 'NAME -> ' ~ @elist.^name;
say 'PERL -> ' ~ @elist.perl;
say 'WHO -> ' ~ @elist.WHO;
say 'VAR -> ' ~ @elist.VAR;
say 'WHICH -> '~ @elist.WHICH;
say 'DEFINED -> ' ~ @elist.defined;
"\nDumping an empty scalar".say;
my $empty;
say 'NAME -> ' ~ $empty.^name;
say 'PERL -> ' ~ $empty.perl;
say 'WHO -> ' ~ $empty.WHO;
say 'WHICH -> ' ~ $empty.WHICH;
say 'DEFINED -> ' ~ $empty.defined;
that produces the following output:
Dumping an empty value
NAME -> Array
PERL -> []
WHO -> Array
VAR ->
WHICH -> Array|94409968050448
DEFINED -> True
Dumping an empty scalar
NAME -> Any
PERL -> Any
WHO -> Any
WHICH -> Any|U94409927114704
DEFINED -> False
Here you can see how Perl 6 is smarter than the developer: an empty array got an attached instance of type
Array
, as expected, and also such instance
is defined: the fact that the array is empty is a property of the array, not of the variable itself.
The same is not true for an empty scalar, that in this case got a
Any
value and is also not defined.
Assigning explicitly a
Nil
value to a scalar produces the very same output:
"\nDumping a Nil value".say;
$empty = Nil;
say 'NAME -> ' ~ $empty.^name;
say 'PERL -> ' ~ $empty.perl;
say 'WHO -> ' ~ $empty.WHO;
say 'WHICH -> ' ~ $empty.WHICH;
say 'DEFINED -> ' ~ $empty.defined;
and you can see the output as
Dumping a Nil value
NAME -> Any
PERL -> Any
WHO -> Any
WHICH -> Any|U94299069706192
DEFINED -> False
Where is my Nil
?
Assigning
Nil
to a variable and getting it is of type
Any
can be confusing at glance, but
the documentation states it clearly:
When assigned to a container,
the Nil value (but not any subclass of Nil)
will attempt to revert the container
to its default value;
if no such default is declared,
Perl 6 assumes Any.
In other words, each time you assign
Nil
to a variable (container) it tries to force the undefined value on the container placing its type to the default one (the one used in the declaration) or
Any
.
This can be better understood with the following example:
my Str $string = Nil;
"\nDumping a Nil Str".say;
say 'NAME -> ' ~ $string.^name;
say 'PERL -> ' ~ $string.perl;
say 'WHO -> ' ~ $string.WHO;
say 'WHICH -> ' ~ $string.WHICH;
say 'DEFINED -> ' ~ $string.defined;
that this time produces a not defined
Str
value:
Dumping a Nil Str
NAME -> Str
PERL -> Str
WHO -> Str
WHICH -> Str|U94701194870208
DEFINED -> False
Containers: the magic behind the everything is an object
The above discussion cannot be complete without speaking about
containers, that are that level of abstraction that makes the
everything is an object statement possible in Perl 6.
The idea is simple:
in Perl 6 the term variable means a name to a container, being the container a reference to the actual value.
That means that a variable is not a user-level object, rather an indirection layer between the user and the value stored in the container.
Each time Perl 6 encounters a new variable, it stores it in a lexical pad (a table of variables, for short) with a ponter to the container instance of such type. This means, for instance, that when a
my Str $string
variable is declared, Perl 6 stores an entry in the variable table with the name
$string
and a pointer to an instance of the container, in this case a
Str
object. This essentially intiializes the variable to the container itself, allowing for introspection and elaboration on the object itself even if the variable has not been assigned to a value.
Once the variable got assigned, the container takes an action to track the content. In the case of a
Scalar container, it simply substitutes itself with the content instance, in the case of a complex container (e.g., an
Array
) it can be append the content to the previous one.
The
[Scalar](https://docs.perl6.org/type/Scalar)
container can be seen via the special
VAR
method:
my Str $empty_string;
my @empty_array;
say 'NAME -> ' ~ $empty_string.^name;
say 'DEFINED -> ' ~ $empty_string.defined;
say 'CONTAINER -> ' ~ $empty_string.VAR.^name;
say "\n";
say 'NAME -> ' ~ @empty_array.^name;
say 'DEFINED -> ' ~ @empty_array.defined;
say 'CONTAINER -> ' ~ @empty_array.VAR.^name;
say "\n";
and it produces an output similar to the following:
NAME -> Str
DEFINED -> False
CONTAINER -> Scalar
NAME -> Array
DEFINED -> True
CONTAINER -> Array
of course, in the case of an array the container is not a
Scalar
.
The Scalar
container enters whenever a $ sigil is used
It is a quite simple rule of thumb: if it seems a scalar, its container is a
Scalar
.
So for instance, the following piece of code:
for ( $empty_string, @empty_array ) -> $_ {
say 'NAME -> ' ~ $_.^name;
say 'DEFINED -> ' ~ $_.defined;
say 'CONTAINER -> ' ~ $_.VAR.^name;
say "\n";
}
does not produce the same output as the former example, as well as does not produce an
Array
container for the array:
NAME -> Str
DEFINED -> False
CONTAINER -> Scalar
NAME -> Array
DEFINED -> True
CONTAINER -> Scalar
The fact is that
every time you assign something to a $-variable it will go thru a Scalar
container, no matter what the actual value is.
This does not mean the content will be lost, and in fact
a good thing about Scalar
containes is that it does delegate pretty much every method call to the content itself.
Summary
In Perl 6
each variable holds a container, the container knows how to manipulate the data inside it. The
Scalar
container is used pretty much everywhere a
$
sigil appears, and it does delegate method calls to its real content (e.g., a
Str
).
When Perl 6 encounters a variable, it does place a container instance into it, allowing for a variable to never be
null, and relying on the
defined-ness of the container content for knowing if it has a value or not.
The special value
Nil
is used as a placeholder for none-value at all, but when it used against a container (i.e., assigned to a variable) it does reset the container to its default value, never appearing therefore within a single variable.