Exploring Perl 6: Signatures, Part 1

| 3 Comments | No TrackBacks

Perhaps one of Perl 5's most glaring oversights is its lack of formal subroutine parameter lists. There are a number of solutions on CPAN that attempt to solve this problem with various syntactic sweeteners of diverse complexity and style. In recent releases of Perl 5, there is even an experimental signature system in the core. But all of them — ultimately — come down to unpacking the magical @_ array into lexical variables for the function to use.

Perl 6 has a sophisticated, robust, and flexible signature system for subroutines and methods. Let's check it out.

Positional parameters

The most basic signature in Perl 6 is a simple list of scalar names to which input parameters will be bound.

sub foo( $a, $b, $c ) { 
    say "a is $a, b is $b, and c is $c" 
}

foo( 42, 'foo', 3+4i );
# a is 42, b is foo, and c is 3+4i

This signature requires exactly three parameters, but they can be of any scalar type. If we try to pass too few or too many, we'll get a fatal error.

foo( 42, 'foo' ) 
# ERROR: Calling foo(Int, Str) will never work with declared signature ($a, $b, $c)

foo( 42, 'foo', 3+4i, "too many" )
# ERROR: Too many positionals passed; expected 3 arguments but got 4

We can make positional parameters optional by suffixing them with a ?. Any optional parameters must appear at the end of the positional parameter list. Putting an optional parameter in the middle will make the compiler justifiably upset with you.

Here is the same function as above, but with $c turned into an optional parameter.

sub foo( $a, $b, $c? ) { 
    say "a is $a, b is $b" ~ ( $c ?? " and c is $c" !! "" ) 
}

foo( 42, 'foo', 3+4i );
# a is 42, b is foo and c is 3+4i

foo( 42, 'foo' );
# a is 42, b is foo

Here we've used the Perl 6 ternary conditional operator ?? !! to check if $c is a true value before trying to turn it into a string. (If no $c was passed, then the sub will receive a value of type Any which causes an error if you attempt to stringify it.)

Checking for the existence of optional parameters with conditionals can become cumbersome and messy. Another, cleaner way to handle it is to use multiple dispatch. Perl 6's multi keyword can be used with subroutines and methods to provide dispatch for multiple calling formats.

multi sub foo ( $a, $b ) { 
    say "a is $a and b is $b" 
}

multi sub foo ( $a, $b, $c ) { 
    say "a is $a, b is $b, and c is $c" 
}

foo( 42, 'foo' );
# a is 42 and b is foo

foo( 42, 'foo', 3+4i );
# a is 42, b is foo, and c is 3+4i

By letting the compiler figure out which version of the sub to call, we've saved ourselves from having to write a tedious conditional and a difficult-to-read string concatenation.

Named parameters

Perl 6 also allows you to specify named parameters, by prefixing them with a :. Since they're named, the order doesn't matter.

sub area( :$x1, :$y1, :$x2, :$y2 ) { 
    say "the area is " ~ ( $x2 - $x1 ) * ( $y2 - $y1 ) 
}

area( x1 => 1, y1 => 1, x2 => 4, y2 => 5 ); # the area is 12
area( x2 => 4, y2 => 5, x1 => 1, y1 => 1 ); # the area is 12
area( :x1(1), :y1(1), :x2(4), :y2(5) ); # the area is 12
area( :x2(4), :y2(5), :x1(1), :y1(1) ); # the area is 12

You can also use either the "adverbial form" :name($val) or the pair constructor name => $val to call a function with named parameters.

The adverbial form provides a nice shortcut if you have variables with the same name as the function arguments:

my ( $x1, $x2, $y1, $y2 ) = ( 1, 4, 1, 5 );
area( :$x1, :$y1, :$x2, :$y2 ) # the area is 12

Named parameters are always optional by default. To make them mandatory, suffix them with a !.

Slurpy parameters

You can create variadic functions by specifying a slurpy array or hash in the signature. A slurpy array will consume any leftover positional parameters that do not match the explicitly-named ones, and a slurpy hash will grab the named ones. You specify a slurpy parameter by prefixing the array or hash with a *. Let's put together what we've seen so far.

sub lots_of_things( $first, $second, $third?, :$foo!, :$bar, *@more, *%named ) { 
    say "I got first = $first, second = $second, and foo = $foo."; 
    say "more positionals = " ~ @more.perl; 
    say "and more named = " ~ %named.perl 
}

lots_of_things( 'one', 'two', 'three', 'four', foo => '42', frobnitz => 'narf' );

Our output from that is:

I got first = one, second = two, and foo = 42.
more positionals = ["four"]
and more named = {:frobnitz("narf")}

New pen

Even more

One of the most exciting aspects of Perl 6 signatures is that they're not just for named functions and methods. In fact, any code block in Perl 6 can take a signature. Consider the humble for-loop:

my @array = ( 4, 7, 3, 3, 2, 6, 7 );
for @array -> $x { 
    say $x;
}

The $x is not just a loop variable, it's actually the signature for the loop's block. That means you can wrap up a lot of functionality within the core features of the Perl 6 language.

This is only a very basic introduction to signatures in Perl 6. In Part 2, we'll take a look at type constraints, return types, default values, and more.

No TrackBacks

TrackBack URL: http://friedo.com/cgi-bin/mt/mt-tb.cgi/34

3 Comments

A little example of mandated named parameters:

##########
#!/usr/bin/env perl6

# named parameters are optional by default; mandate them with '!' suffix

sub triangle (:$base!, :$height!) { say "Area of triangle is " ~ ( $base * $height / 2); }

say "Mandated named parameters: pair-constructor syntax";
triangle( base => 10, height => 15 );
triangle( height => 15, base => 10 );
say "";
say "Mandated named parameters: adverbial syntax";
triangle( :base(10), :height(15) );
triangle( :height(15), :base(10) );

##########

Output:

##########
Mandated named parameters: pair-constructor syntax
Area of triangle is 75
Area of triangle is 75

Mandated named parameters: adverbial syntax
Area of triangle is 75
Area of triangle is 75
##########

Thank you very much.
Jim Keenan

"The $x is not just a loop variable, it's actually the signature for the loop's block. That means you can wrap up a lot of functionality within the core features of the Perl 6 language."



Could you provide an example of that wrapping of functionality?



Thank you very much.
Jim Keenan


Leave a comment

About this Entry

This page contains a single entry by Mike Friedman published on January 14, 2016 3:22 PM.

Exploring Perl 6: Meta Operators was the previous entry in this blog.

Exploring Perl 6: Signatures, Part 2 is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.

Categories

  • Exploring Perl 6

Pages

Powered by Movable Type 5.14-en