Science and technology

Calling subs and typing in Perl 6

This is the ninth article in a series about migrating code from Perl 5 to Perl 6. This article examines the refined variations in visibility of subroutines between Perl 5 and Perl 6 and the (gradual) typing core function of Perl 6.

This article assumes you are accustomed to signatures—if you happen to’re not, learn How subroutine signatures work in Perl 6, the fourth article on this collection, earlier than you proceed.

Visibility of subroutines

In Perl 5, a named subroutine will, by default, be seen throughout the scope of the bundle through which it’s outlined, whatever the scope the place the definition takes place:

# Perl 5

    sub foo "bar"       # seen exterior of this scope

say foo();
# bar

In Perl 6, a named subroutine is seen solely throughout the lexical scope through which it’s outlined:

# Perl 6

say foo();
# ===SORRY!=== Error whereas compiling ...
# Undeclared routine:
#     foo used at line ...

Note that SORRY! within the Perl 6 error message signifies that the subroutine foo cannot be discovered at compile time. This is a really helpful function that helps forestall typos in subroutine names when writing invocations of the subroutine.

You may think about subroutine definitions in Perl 6 to at all times have a my in entrance, much like defining lexical variables. Perl 5 additionally has a (beforehand experimental) lexical subroutine function, which must be particularly activated in variations decrease than Perl 5.26:

# Perl 5.18 or greater
no warnings 'experimental::lexical_subs';
use function 'lexical_subs';

    my sub foo "bar"   # restrict visibility to this scope

say foo();
# Undefined subroutine &fundamental::foo referred to as at ...

It is feasible in each Perl 5 and Perl 6 to prefix the subroutine definition with an our scope indicator, however the result’s subtly totally different: In Perl 5, this makes the subroutine seen exterior the scope, however this is not the case in Perl 6. In Perl 6, lookups of subroutines are at all times lexical: using our on subroutine declarations (no matter scope) permits the subroutine to be referred to as from exterior the namespace through which it’s outlined:

# Perl 6
module Foo
    our sub bar() "baz"  # make sub seen from the surface

say Foo::bar();
# baz

This would fail with out the our. In Perl 5, any subroutine will be referred to as from exterior the namespace the place it’s outlined:

# Perl 5
package Foo
say Foo::bar();
# baz

In Perl 5, the names of subroutines which are meant to be “private” (i.e., referred to as from inside that scope solely and never from exterior) often begin with an underscore. But that will not cease them from being referred to as from the surface. In Perl 6, subroutines that aren’t meant to be referred to as from the surface are merely invisible.

The our on a subroutine definition in Perl 6 not solely signifies that the subroutine will be referred to as from the surface; it additionally signifies that it will likely be exported whether it is a part of a module being loaded. In a future article in regards to the creation of modules and module loading, I’ll go into exporting subroutines.

Calling a subroutine

When you name a subroutine in a Perl 5 with out subroutine signatures enabled, it’s going to name the subroutine if it exists (decided at runtime) and move the parameters into @_ contained in the subroutine. Whatever occurs to the parameters contained in the subroutine is fully as much as the subroutine (see How subroutine signatures work in Perl 6).

When a subroutine known as in Perl 6, it performs further checks to see whether or not the arguments handed to the subroutine match the parameters the subroutine expects earlier than it calls the subroutine’s code. Perl 6 tries to do that as early as attainable—if it determines that a name to a subroutine won’t ever succeed, it’s going to let you know at compile time:

# Perl 6
sub foo() "bar"    # subroutine not taking any parameters
say foo(42);           # attempt calling it with one argument
# ===SORRY!=== Error whereas compiling ...
# Calling foo(Int) won't ever work with declared signature ()

Note that the error message mentions the kind of worth (Int) being handed as an argument. In this case, calling the subroutine will fail as a result of the subroutine would not settle for any argument being handed to it (declared signature ()).

Other signature options

Apart from specifying positional and named parameters in a signature, you may as well specify what kind these parameters ought to be. If the parameter kind would not smartmatch with the argument kind, it will likely be rejected. In this instance, the subroutine expects a single Str argument:

# Perl 6
sub foo(Str $who) "Hello $who"  # subroutine taking a Str parameter
say foo(42);                        # attempt calling it with an integer
# ===SORRY!=== Error whereas compiling ...
# Calling foo(Int) won't ever work with declared signature (Str $who)

It checks each the variety of required parameters and the sort. Unfortunately, it’s not at all times attainable to reliably see this at compilation time. But there’s nonetheless the test performed at runtime when binding the argument to the parameter:

# Perl 6
sub foo(Str $who) "Hello $who"  # subroutine taking a Str parameter
my $reply = 42;
say foo($reply);                   # attempt calling it with a variable
# Type test failed in binding to parameter '$who'; anticipated Str however acquired Int (42)
#   in sub foo at ...

However, if Perl 6 is aware of the kind of variable being handed to the subroutine, it will possibly decide at compile time that the decision won’t ever work:

# Perl 6
sub foo(Str $who) "Hello $who"  # subroutine taking a Str parameter
my Int $reply = 42;
say foo($reply);                   # attempt calling it with an Int variable
# ===SORRY!=== Error whereas compiling ...
# Calling foo(Int) won't ever work with declared signature (Str $who)

It ought to be clear that utilizing typing in your variables and parameters allows Perl 6 that can assist you discover issues faster!

Gradual typing

The above is often referred to as gradual typing. Perl 6 at all times performs kind checks at runtime (dynamic typing). But if it will possibly decide at compile time that one thing won’t work, it’s going to let you know so. This is often referred to as static typing.

If you are coming from Perl 5 and have expertise with Moose (and particularly MooseX::Types), you could fear in regards to the efficiency implications of including kind data to your code. This will not be a priority in Perl 6, as kind checks at all times happen in Perl 6 with each project to a variable or binding of a parameter. That is as a result of if you don’t specify a sort, Perl 6 will implicitly assume the Any kind, which smartmatches with (virtually) every little thing in Perl 6. So, if you happen to’re writing:


You have in actual fact written:

# Perl 6
my Any $foo = 42;

And the identical goes for a parameter to a subroutine:

# Perl 6
sub foo($bar)

Which is in actual fact:

# Perl 6
sub foo(Any $bar)

Adding kind data not solely helps discover errors in your program, it additionally permits the optimizer to make better-informed choices about what it will possibly optimize and how you can greatest optimize it.

Defined or not

If you specify a variable in Perl 5 however do not assign it, it incorporates the undefined worth (undef):

# Perl 5
my $foo;
say defined($foo) ? "defined" : "NOT defined";
# NOT outlined

This will not be a lot totally different in Perl 6:

# Perl 6
my $foo;
say defined($foo) ?? "defined" !! "NOT defined";
# NOT outlined

So, you may specify which kinds of values are acceptable in a variable and as a parameter. But what occurs if you happen to do not assign such a variable?

# Perl 6
my Int $foo;
say defined($foo) ?? "defined" !! "NOT defined";
# NOT outlined

The worth inside such a variable remains to be not outlined, as in Perl 5. However, if you happen to simply need to present the contents of such a variable, it’s not undef, as it might be in Perl 5:

# Perl 6
my Int $foo;
say $foo;
# (Int)

What you see is the illustration of a kind object in Perl 6. Unlike Perl 5, Perl 6 has a large number of typed undefs. Each class that’s outlined, or which you outline your self, is a sort object.

# Perl 6
class Foo
say defined(Foo) ?? "defined" !! "NOT defined";
# NOT outlined

If, nonetheless, you instantiate a sort object, often with .new, it turns into an outlined object, as anticipated:

# Perl 6
class Foo
say defined(Foo.new) ?? "defined" !! "NOT defined";
# outlined

Type smileys

If you specify a constraint on a parameter in a subroutine, you may as well point out whether or not you need a outlined worth of that kind or not:

# Perl 6
sub foo(Int:D $bar)

The 😀 mixed with the Int signifies that you really want a Defined worth of the Int kind. Because 😀 can also be the emoji for a giant smile, this ornament on the sort known as a “type smiley.” So what occurs if you happen to move an undefined worth to such a subroutine?

# Perl 6
sub foo(Int:D $bar)   # solely settle for situations of Int
foo(Int);                     # name with a sort object
# Parameter '$bar' of routine 'foo' should be an object occasion of
# kind 'Int', not a sort object of kind 'Int'.  Did you overlook a '.new'?

Careful readers might understand that this could create a compile-time error. But alas, it hasn’t (but). Although error messages are recognized to be fairly superior in Perl 6, there may be nonetheless quite a lot of work to make them even higher (and extra well timed, on this case).

You also can use the 😀 kind smiley on variable definitions to make sure that you present an initialization for that variable:

# Perl 6
my Int:D $foo;                # lacking initialization
# ===SORRY!=== Error whereas compiling ...
# Variable definition of kind Int:D requires an initializer

Other kind smileys are :U (for unoutlined) and :_ (for do not care, which is the default):

# Perl 6
sub foo(Int:U $bar)   # solely settle for Int kind object
foo(42);                      # name with an occasion of Int
# Parameter '$bar' of routine 'foo' should be a sort object of kind 'Int',
# not an object occasion of kind 'Int'.  Did you overlook a 'multi'?

Hmmm… what’s this multi that appears to be forgotten? Well, that is for the subsequent article on this collection!

Summary

Subroutines in Perl 6 are, by default, seen solely within the lexical scope the place they’re outlined. Even prefixing our won’t make them seen exterior of that lexical scope, but it surely does enable a subroutine to be referred to as from exterior the scope with its full bundle title (Foo::bar() for a subroutine bar in a bundle bar).

Perl 6 means that you can use gradual typing to make sure the validity of arguments to subroutines or assignments to variables. This does not incur any further runtime prices. Adding typing to your code even permits the compiler to catch errors at compile time, and it permits the optimizer to make higher choices about optimizing throughout runtime.

Most Popular

To Top