How naming of variables works in Perl 6

How naming of variables works in Perl 6

In the fifth article in this series comparing Perl 5 to Perl 6, find out about using sigils in Perl 6.

woman programming
Image by : 
WOCinTech Chat. Modified by Opensource.com. CC BY-SA 4.0
x

Get the newsletter

Join the 85,000 open source advocates who receive our giveaway alerts and article roundups.

In the first four articles in this series comparing Perl 5 to Perl 6, we looked into some of the issues you might encounter when migrating code, how garbage collection works, why containers replaced references, and using (subroutine) signatures in Perl 6 and how these things differ from Perl 5.

Here, in the fifth article, we will look at the subtle differences in sigils (the symbols at the start of a variable name) between Perl 5 and Perl 6.

An overview

Let's start with an overview of sigils in Perl 5 and Perl 6:

Sigil Perl 5 Perl 6
@ Array Positional
% Hash Associative
& Subroutine Callable
$ Scalar Item
* Typeglob n/a

@ (Array vs. Positional)

When you define an array in Perl 5, you create an expandable list of scalar values and give it a name with the sigil @:

# Perl 5
my @foo = (1,2,3);
push @foo, 42;
say for @foo;  # 1␤2␤3␤42␤

When you define an array in Perl 6, you create a new Array object and bind it to the entry by that name in the lexical pad. So:

# Perl 6
my @foo = 1,2,3;
push @foo, 42;
.say for @foo;  # 1␤2␤3␤42␤

is functionally the same as in Perl 5. However, the first line is syntactic sugar for:

# Perl 6
my @foo := Array.new( 1,2,3 );

This binds (rather than assigns) a new Array object to the lexically defined name @foo. The @ sigil in Perl 6 indicates a type constraint: if you want to bind something into a lexpad entry with that sigil, it must perform the Positional role. It's not hard to determine whether a class performs a certain role using smartmatch:

# Perl 6
say Array ~~ Positional;   # True

You could argue that all arrays in Perl 6 are implemented in the same way as tied arrays are implemented in Perl 5. And that would not be far from the truth. Without going too deep into the specifics, a simple example might clarify this. The AT-POS method is one of the key methods of a class implementing the Positional role. This method is called whenever a single element needs to be accessed. So, when you write:

say @a[42];

you are executing:

say @a.AT-POS(42);

Of course, this is not the only method you could implement; there are many more.

Rather than having to bind your class performing the Positional role, there's a special syntax using the is trait. So instead of having to write:

# Perl 6
my @a := YourClass.new( 1,2,3 );

you can write:

# Perl 6
my @a is YourClass = 1,2,3;

In Perl 5, tied arrays are notoriously slow compared to "normal" arrays. In Perl 6, arrays are similarly slow at startup. Fortunately, Rakudo Perl 6 optimizes hot-code paths by inlining and "just in timing" (JITing) opcodes to machine code where possible. (Thanks to advancements in the optimizer, this happens sooner, more often, and better).

% (Hash vs. Associative)

Hashes in Perl 6 are implemented similarly to arrays; you could also consider them a tied hash (using Perl 5 terminology). Instead of the Positional role used to implement arrays, the Associative role should be used to implement hashes.

Again, a simple example might help. The AT-KEY method is one of the key methods of a class implementing the Associative role. This method is called whenever the value of a specific key needs to be accessed. So, when you write:

say %h<foo>

you are executing:

say %h.AT-KEY("foo");

Of course, there are many other methods you could implement.

& (Subroutine vs. Callable)

In Perl 5, there is only one type of callable executable code, the subroutine:

# Perl 5
sub frobnicate { shift ** 2 }

And, if you want to pass on a subroutine as a parameter, you need to get a reference to it:

# Perl 5
sub do_stuff_with {
    my $lambda = shift;
    &$lambda(shift);
}
say do_stuff_with( \&frobnicate, 42 );  # 1764

In Perl 6, multiple types of objects can contain executable code. What they have in common is that they consume the Callable role.

The & sigil forces binding to an object performing the Callable role, just like the % sigil does with the Associative role and the @ sigil does with the Positional role. An example very close to Perl 5 is:

# Perl 6
my &foo = sub ($a,$b) { $a + $b }
say foo(42,666);  # 708

Note that even though the variable has the & sigil, you do not need to use it to execute the code in that variable. In fact, if you ran the code in a BEGIN block, there would be no difference compared to an ordinary sub declaration:

# Perl 6
BEGIN my &foo = sub ($a,$b) { $a + $b } # same as sub foo()

In contrast to Perl 5, in Perl 6, a BEGIN block can be a single statement without a block, so it shares its lexical scope with the outside. But we'll look more at that in a future article.

The main advantage to using an & sigilled variable is that it will be known at compile time that there will be something executable in there, even if that something isn't yet known.

There are other ways to set up a piece of code for execution:

# Perl 6
my &boo = -> $a, $b { $a + $b }  # same, using a Block with a signature
my &goo = { $^a + $^b }          # same, using auto-generated signature
my &woo = * + *;                 # same, using Whatever currying

If you'd like to know more:

The one you use depends on the situation and your preferences.

Finally, you can also use the & sigil inside a signature to indicate that the callee wants something executable there. Which brings us back to the first two code examples in this section:

# Perl 5
sub frobnicate { shift ** 2 }
sub do_stuff_with {
    my $lambda = shift;
    &$lambda(shift);
}
say do_stuff_with( \&frobnicate, 42 );  # 1764
# Perl 6
sub frobnicate { $^a ** 2 }
sub do-stuff-with(&lambda, $param) { lambda($param) }
say do-stuff-with( &frobnicate, 42 );  # 1764

Note that in Perl 6 you don't need to take a reference; you can simply pass the code object (as indicated by the & sigil) as a parameter.

$ (Scalar vs. Item)

Compared to the @, % and & sigils, the $ sigil is a bit bland. It doesn't enforce any type checks, so you can bind it to any type of object. Therefore, when you write:

# Perl 6
my $answer = 42;

something like this happens:

# Perl 6
my $answer := Scalar.new(42);

except at a very low level. Therefore, this code won't work, in case you wondered. And that's all there is to it when you're declaring scalar variables.

In Perl 6, $ also indicates that whatever is in there should be considered a single item. So, even if a scalar container is filled with an Array object, it will be considered a single item in situations where iteration is required:

# Perl 6
my @foo = 1,2,3;
my $bar = Array.new(1,2,3);  # alternately: [1,2,3]
.say for @foo;  # 1␤2␤3␤
.say for $bar;  # [1 2 3]

Note that the latter case does only one iteration vs. three in the former case. You can indicate whether you want something to iterate or not by prefixing the appropriate sigil:

# Perl 6
.say for $@foo;  # [1 2 3] , consider the array as an item
.say for @$bar;  # 1␤2␤3␤  , consider the scalar as a list

But maybe this brings us too far into line-noise land. Fortunately, there are also more verbose equivalents:

# Perl 6
.say for @foo.item;  # [1 2 3] , consider the array as an item
.say for $bar.list;  # 1␤2␤3␤  , consider the scalar as a list

* (Typeglobs)

As you may have noticed, Perl 6 does not have a * sigil nor the concept of typeglobs. If you don't know what typeglobs are, you don't have to worry about this. You can get by very well without having to know the intricacies of symbol tables in Perl 5 (and you can skip the next paragraph).

In Perl 6, the sigil is part of the name stored in a symbol table, whereas in Perl 5 the name is stored without the sigil. For example, in Perl 5, if you reference $foo in your program, the compiler will look up foo (without sigil), then fetch the associated information (which is an array), and look up what it needs at the index for the $ sigil. In Perl 6, if you reference $foo, the compiler will look up $foo and directly use the information associated with that key.

Please do not confuse the * used to indicate slurpiness of parameters in Perl 6 with the typeglob sigil in Perl 5—they have nothing to do with each other.

Sigilless variables

Perl 5 does not support sigilless variables out of the box (apart from maybe left-value subroutines, but that would be very clunky indeed).

Perl 6 does not directly support sigilless variables either, but it does support binding to sigilless names by prefixing a backslash (\) to the name in a definition:

# Perl 6
my \the-answer = 42;
say the-answer;  # 42

Since the right-hand side of the assignment is a constant, this is basically the same as defining a constant:

# Perl 5
use constant the_answer => 42;
say the_answer;  # 42
# Perl 6
my constant the-answer = 42;
say the-answer;  # 42

It's more interesting if the right-hand side of a definition is something else. Something like a container! This allows for the following syntactic trick to get sigilless variables:

# Perl 6
my \foo = $ = 41;                # a sigilless scalar variable
my \bar = @ = 1,2,3,4,5;         # a sigilless array
my \baz = % = a => 42, b => 666; # a sigilless hash

This basically creates nameless lexical entities (a scalar, an array, and a hash), initializes them using the normal semantics, and then binds the resulting objects (a Scalar container, an Array object, and a Hash object) to the sigilless name, which you can use as any other ordinary variable in Perl 6.

# Perl 6
say ++foo;     # 42
say bar[2];    # 3
bar[2] = 42;
say bar[2];    # 42
say baz<a b>;  # (42 666)

Of course, by doing this you will lose all the advantages of sigils, specifically with regard to interpolation. You will then always need to use { } in interpolation.

# Perl 6
say "The answer is {the-answer}.";  # The answer is 42.

The equivalent is more cumbersome in most versions of Perl 5:

# Perl 5
say "The answer is @{[the_answer]}.";  # The answer is 42.

Summary

All variables in Perl 6 could be considered tied variables when thinking about them using Perl 5 concepts. This makes them somewhat slow initially. But runtime optimizations and JITting of hot-code paths (at one point to machine code) already make it faster than Perl 5 variables in some benchmarks.

The @, %, and & in Perl 6 do not create any specific objects, rather they indicate a type constraint that will be applied to the object a name is bound to. The $ sigil is different in that respect, as there is no type constraint to be enforced.

The @ and $ prefixes indicate listification and itemization, respectively, although it's probably more readable to use the .list and .item methods instead.

With a few syntactic tricks, you can program Perl 6 without using any sigils in variable names.

Perl 6 logo butterfly

Examine the issues you may encounter when porting Perl 5 programs to Perl 6.
Garbage illustration

In the second article in this series comparing Perl 5 to Perl 6, learn how to handle object...
Data container block with hexagons

In the third article in this series comparing Perl 5 to Perl 6, learn how to handle references,...
women programming

In the first article in this series comparing Perl 5 to Perl 6, we looked into some of the issues...

About the author

Elizabeth Mattijsen
Elizabeth Mattijsen - Elizabeth Mattijsen has been programming for a living since 1978 in various (mostly now defunct) programming languages before she started programming in Perl 4. In 1994 she started the first commercial web site development company in the Netherlands, using Perl 5 as the main programming language. From 2003 onwards, she was involved in the rapid growth of an online Hotel Reservation service. In 2012 she got more directly involved in the development of Rakudo Perl 6. From 2015 she has also...