Skip to content

Instantly share code, notes, and snippets.

@moritz
Created March 2, 2013 12:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save moritz/5070740 to your computer and use it in GitHub Desktop.
Save moritz/5070740 to your computer and use it in GitHub Desktop.
Containers, Assignment and Binding

Perl 6 Containers: a low-level approach

This article started as a conversion on IRC explaining the difference between the Array and the List type in Perl 6. It explains the levels of indirection involved in dealing with variables and container elements.

What is a variable?

Some people like to say "everything is an object", but in fact a variable is not a user-exposed object in Perl 6.

When the compiler encounters a variable declaration like my $x, it registers it in some internal symbol table. This internal symbol table is used to detect undeclared variables, and to tie the code generation for the variable to the correct scope.

At run time, a variable appears as an entry in a lexical pad, short lexpad. This is a per-scope data structure that stores a pointer for each variable.

In the case of my $x, the lexpad entry for the variable $x is a pointer to an object of type Scalar, usually just called the container.

Scalar containers

Although objects of type Scalar are everywhere in Perl 6, you usually never see them directly as objects, because most operations decontainerize, which means they act on the Scalar container's contents instead of the container itself.

In a code like

my $x = 42;
say $x;

the assignment $x = 42 stores a pointer to the Int object 42 in the scalar container to which the lexpad entry for $x points.

The assignment operator asks the container on the left to store the value on its right. What exactly that means is up to the container type. For Scalar it means "replace the previously stored value with the new one".

Note that subroutine signatures allow passing around of containers:

sub f($a is rw) {
    $a = 23;
}
my $x = 42;
f($x);
say $x;         # 23

Inside the subroutine, the lexpad entry for $a points to the same container that $x points to outside the subroutine. Which is why assignment to $a also modifies the contents of $x.

Binding

Next to assignment, Perl 6 also supports binding. When binding a value or a container to a variable, the lexpad entry of the variable is modified (and not just the container it points to). If you write

my $x := 42;

then the lexpad entry for $x directly points to the Int 42. Which means that you cannot assign to it anymore:

$ perl6 -e 'my $x := 42; $x = 23'
Cannot modify an immutable value
   in block  at -e:1

You can also bind variables to other variables:

my $a = 0;
my $b = 0;
$a := $b;
$b = 42;
say $a;         # 42

Here after the initial binding, the lexpad entries for $a and $b both point to the same scalar container, so assigning to one variable also changes the contents of the other.

You've seen this situation before: it is exactly what happened with the signature parameter marked as is rw.

Scalar Containers and Listy Things

There are a number of positional container types with slightly different semantics in Perl 6. The most basic one is Parcel, short for Parenthesis cell. It is created by the comma operator, and often delimited by round parenthesis -- hence the name.

say (1, 2, 3).WHAT;     # (Parcel)

A parcel is immutable, which means you cannot change the number of elements in a parcel. But if one of the elements happens to be a scalar container, you can still assign to it:

my $x = 42;
($x, 1, 2)[0] = 23;
say $x;                 # 23
($x, 1, 2)[1] = 23;     # Error: Cannot modify an immutable value

So the parcel doesn't care about whether its elements are values or containers, they just store and retrieve whatever was given to them.

A List has the same attitude of indifference towards containers. But it allows modifying the length (for example with push, pop, shift and unshift), and it is also lazy.

An Array is just like a list, except that it forces all its elements to be containers. Thus you can say

my @a = 1, 2, 3;
@a[0] = 42;
say @a;         # 42 2 3

and @a actually stores three scalar containers. @a[0] returns one of them, and the assignment operator replaces the integer value stored in that container with the new one, 42.

Assigning and Binding to Array Variables

Assigning to a scalar variable and to an array variable both do basically the same thing: discard the old value(s), and enter some new value(s).

Still it's easy to observe how different they are:

my $x = 42; say $x.WHAT;    # (Int)
my @a = 42; say @a.WHAT;    # (Array)

This is because the Scalar container type hides itself well, but Array makes no such effort.

To place a non-Array into an array variable, binding works:

my @a := (1, 2, 3);
say @a.WHAT;                # (Capture)
@TimToady
Copy link

TimToady commented Mar 2, 2013

Typo in first sentence.

@FROGGS
Copy link

FROGGS commented Mar 2, 2013

+1

@tadzik
Copy link

tadzik commented Mar 3, 2013

Well written and explaining things clearly. Awesome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment