Perl 6 Colonoscopy

by Arne Sommer

Perl 6 Colonoscopy

I promise, this will not hurt. Much...

In this article we'll look into colons in Perl 6, where to place them, and what they mean.

Sigils

But first a short detour. A newcomer to the Perl 6 universe cannot miss the significance of sigils when dealing with variables. They are:
  • $ (Anything)
  • @ (Array)
  • % (Hash)
  • & (Callable)
The sigil is part of the variable name, and is not changed depending on the context (as in Perl 5):
WhatPerl 5 Perl 6 Perl 6 Alternate Syntax
Array @a @a
Array element     $a[0] @a[0]
Array slice @a[0,1] @a[0,1]
Hash %h %h
Hash element $h{'a'} %h{'a'} %h<a>
Hash slice @h{'a', 'b'}     %h{'a', 'b'}     %h<a b>

Perl 6 also has Twigils, a secondary character between the sigil and the variable name, that slightly alters the meaning of the variable. We'll not get into that in this article (with one exception, the colon).

See docs.perl6.org/language/variables for more information about Variables, Sigils and Twigils.

Namespaces

A double colon (::) is used to address a name (variable, procedure or class) in another namespace.

Let us say that the have a module Foo with a procedure bar and a variable $bar. We can access them like this (if the module has made them available for outside code):

use Foo;

Foo::bar;       # A procedure call

say $Foo::bar;  # Accessing a variable

We can also use namespaces directly (but it isn't recommended):

File: namespace
my $a = 10;
my $FOO::BAR::a = 20;

say $a + $FOO::BAR::a;  # -> 30

Two colons on their own work as well, and gives us access to meta information about the current scope (as far as I understand it):

> say ::
PseudoStash.new((!UNIT_MARKER => (!UNIT_MARKER), $=pod => [],
  $?PACKAGE => (GLOBAL), $_ => (Any), ::?PACKAGE => (GLOBAL),
  EXPORT => (EXPORT), GLOBALish => (GLOBAL)))

See docs.perl6.org/type/PseudoStash for information about the PseudoStash type, but be prepared to be (or stay) confused.

Versioned Modules

A module can have an optional version and/or author value (and some others). They are affixed after the module name with a single colon, like this:

module Foo:ver<1.00>:auth { ... }

Inline::Perl5

This module makes it possible to use Perl 5 modules. It is used indirectly like this, where «Foo::Bar» is a Perl 5 module:

use Foo::Bar:from<Perl5>;

This works for other languages as well. See modules.perl6.org/search/?q=inline for a list of available languages. The support may be incomplete, so read the documentation.

Labels

A label is just a label. It doesn't do anything by itself.

We cannot use it to jump around in the code, as Perl 6 doesn't have a goto command. But we can use it to jump out of loops (or around in them), with the last, next or redo commands.

Consider the last command without using a label:

File: last
for 1 .. 10 -> $a
{
  for 1 .. 10 -> $b
  {
    last if $b == 5;
    say "$a $b";
  }
}

The last will exit the inner loop if the condition is met. We can get it to act on another level if we use a label:

File: last2
outer: for 1 .. 10 -> $a
{
  inner: for 1 .. 10 -> $b
  {
    last outer if $b == 5;
    say "$a $b";
  }
}

This time it exits the outer loop (and the program, as there isn't any more code to execute) when the condition is met.

Upper Case letters are normally used for labels, but this will cause an error if you happen to choose a name that is already in use internally by Perl 6. The label itself isn't a problem, as the trailing colon tells the compiler that it is a label. The usage, where it is used as a bareword causes the problem, and there is no way to fix it.

Some examples:

  • OUTER, which seems perfectly logical as a label name, will give the run time error («Cannot resolve caller next(OUTER:U); ...»), as OUTER is a built-in package name and Perl 6 isn't clever enough to spot that it is used as a label in this case.
  • LAST isn't equally tempting to use, but will give the compilation error («Missing block»), as LAST is a Phaser, expecting a block after it.

See docs.perl6.org/language/packages#Pseudo-packages for more information about Package Names to avoid.

See docs.perl6.org/language/phasers for more information about Phasers.

If you get an error message when using a label, even if you have avoided reserved packagae Names and Phasers, try changing the name by e.g. appending a digit to it. Or use lower case letters.

Numbers

We can specify numbers in any base (from 2 to 16) like this;

my $b = :2<11110000>;  # -> 240   ## A binary (base 2) number
my $a = :16<FF>;       # -> 255   ## A hexadecimal (base 16) number

Note that we can use parens and quotes if we want to make it look like a procedure call:

my $b = :2("11110000");  # -> 240   ## A binary (base 2) number

Pair

A pair is a single key and value. They are the building blocks of Hashes and named parameters (which we'll get back to).

We can construct a Pair in several ways, using the «fat arrow» (=>):

Pair('key' => 'value')
('key' => 'value')
'key' => 'value'
key => 'value'    # As long as the key doesn't contain spaces.

We don't have to use the «fat arrow»:

Pair.new('key', 'value')

We can use the special colon syntax, as long as the key doesn't contain spaces:

:key<value>  # The same as «key => 'value'»

We can use parens as well:

:foo(127) # The same as «foo => 127»

If the key uses letters, and the value digits only, we can do this:

:127foo  # The same as  «foo => 127»

If the value is a Boolean, we can shorten it even further:

:key    # The same as «key => True»
:!key   # The same as «key => False»

Variables

There are three possible locations where we can add a colon to a variable ($variable):
  • Before the sigil (:$variable)
  • Between the sigil and variable name ($:variable)
  • After the variable name ($variable:)
And they are all legal.

:$variable

This is a named variable used in a procedure call (or a Pair constructor, as described in the end of this section):

sub foo (:$one, :$two)
{
  return $one + 2 * $two;
}

say foo(one => 12, two =>  3);  # -> 18
say foo(two =>  3, one => 12);  # -> 18

The order doesn't matter.

If we give the variable the same name as the argument, we can use a short form (shown on the last line):

sub foo (:$one, :$two)
{
  return $one + 2 * $two;
}

my $one = 12;
my $two = 3;

say foo(one => $one, two =>  $two);  # -> 18
say foo(:$one, :$two);               # -> 18

Named variables are useful when we have several parameters, as it is possible to mix up the order with normal positional parameters. It is also handy when we have optional parameters, as we can specify just the one(s) we want to use.

If the value of the named argument is True, we can drop the explicit value:

sub debug (:$debug)
{
  say "Debug" if $debug;
  return 32;
}

my $x = foo(:debug);

And we can negate it (with an exclamation mark between the colon and the name), to pass False:

my $x = foo(:!debug);

Named variables are optional by default, but we can make them mandatory by adding a trailing ! (exclamation mark) in the procedure head.

We can turn any variable into a Pair, with the variable name as the key:

my $age = 14;
my $p = :$age; 
say $p; # -> age => 10

$:variable

This is a named placeholder variable used in procedures where we skipped declaring the arguments in the procedure head:

sub foo
{
  return $:one + 2 * $:two;
}

say foo(one => 12, two => 3);  # -> 18

Note that the names must match - and it will only work if the procedure is specified without a signature (argument list)!

This is the one where the colon is a Twigil.

(We can also use placeholder variables for positional arguments, with the ^ Twigil. See docs.perl6.org/language/variables#The_^_twigil for more information.)

$variable:

If we have an object $foo and want to call a method bar on it, the usual way is this:

$foo.bar;

But we can also use a more procedure like syntax, like this:

bar($foo:);

Method Call with arguments

If we have an object $foo and want to call a method bar on it with some arguments, the usual way is this:

class foo
{
  ...;
    
  method bar ($abc, $def) { ... }
}
    
my $foo1 = foo.new;

$foo1.bar(1, 2);

It isn't possible to skip the parens (as we can with a procedure call), but we can use this colon syntax:

$foo1.bar: 1, 2;

The procedure like syntax works here as well, with parens:

bar($foo1: 1, 2);

:= (Binding)

A normal variable is a named container (also called a bucket) that can contain one ore more values, depending on the type.

Binding skips the container, and works directly with the value. When used on a value, this gives a constant:

my $a := 10;

say $a;  # -> 10;

$a++;    # Fails.
Cannot resolve caller postfix:<++>(Int:D); the following candidates
match the type but require mutable argument ...

If we bind to another variable, we get an alias:

my $a = 10;

my $b := $a; say $b;  # -> 10;
$a++; say $b;         # -> 11;
$b++; say $a;         # -> 12;

Incrementing $b works, because it has been binded to the container we got from $a.

=:= (Container Identity)

This operator returns True if both argumentes are bound to the same container:

my $a = 10;

my $b  = 10; say $a =:= $b;  # -> False
my $c := $a; say $a =:= $c;  # -> True

It works even if we don't have any containers (as they are bound to the same value):

my $a := 10; my $b := 10;

say $a =:= $b;  # -> True

::?CLASS

This Compile Time variable tells us the name of the class we are in:

File: class-doublecolon
class FOO
{
  method bar { return ::?CLASS; }
}

my FOO $foo = FOO.new;

say $foo.bar;  # -> (FOO)

It fails if we are not in a class:

say ::?CLASS;
===SORRY!=== Error while compiling:
No such symbol '::?CLASS'

::($variable)

Use this syntax to turn a string into a variable name at runtime:

File: variable-doublecolon
my ($a1, $a2, $a3, $a4, $a5, $a6, $a7, $a8, $a9, $a10) = <12 9 8 27 75 5 18 21 36 99>;

say "$_: ", ::{ '$a' ~ $_ } for 1 .. 10;

The single quotes are essential, as we definitely do not want the string to be interpolated (as $a doesn't exist).

Use defined if you want to check that the variable exists (is defined), as a «normal» lookup doesn't fail:

say ::{'$a11'};          # -> Nil
say ::{'$a11'}.defined;  # -> False
say ::{'$a10'}.defined;  # -> True

We can use it to look up variables in other scopes as well. See docs.perl6.org/language/packages#Looking_up_names for more information about this topic.

::= (Read Only Binding)

This hasn't been implemented yet.

Commercial Break

This article was written as part of the work on my coming course «Beginning Perl 6».