Skip to content

Latest commit

 

History

History
464 lines (321 loc) · 13.6 KB

subs-n-sigs.pod

File metadata and controls

464 lines (321 loc) · 13.6 KB

A subroutine is a piece of code that performs some specific task. It may take some arguments, which are bits of data that it will work with. The expected arguments are described by a signature. It may also produce some result, which is known as a return value.

You have already seen some simple subroutines in the first chapter, and looked at operators in the second chapter, which in a sense are subrouties that are parsed in an interesting way. However, these just scratch on the surface of what's possible.

Declaring A Subroutine

Most often, a subroutine is declared with a name. This name can then be used to call the subroutine.

By default, this name is lexically scoped. This means that, just like any variable declared with my, it only exists within the current block. Therefore, unless it is explicitly marked as being exported in some way, you can have a good idea of where the sub might be called from. If you want to sub to be more widely available, you can use our to put it in the package.

While all of the subs we have seen so far have names, the name is in fact optional. In Perl 6, subs are objects and can be passed around and stored in data structures just like any other bit of data. For example, if we wanted to make a little ASCII art dancing figure, we could build up a hash. The keys are names of the dance moves, and the values are anonymous subroutines.

From the output of this program, you can observe that doing the YMCA dance in ASCII art looks just about as bad as doing it in real life.

Adding Signatures

A sub's signature performs two roles. First, it declares what arguments may or must be passed to the subroutine. Second, it declares what variables in the sub the arguments should be bound to. Perl 6 signatures provide a lot of power in both of these areas, allowing you to constrain the values that may be passed in a whole range of ways and also to dig into data structures and extract the parts that are of interest.

The basics

In its most simple form, a signature is a comma seperated list of variable names that incoming arguments should be bound to.

Our use of the term bound over assigned is signficant here. The variables in your signature really are read-only references to the arguments that were passed. This means that you can not modify them within the sub. If this is too limiting, then you have two different ways to relax this restriction.

Marking a parameter as is rw means that you are allowed to modify the argument that was passed in. Doing so modifies the original thing that was passed. If a literal or some other constant value was passed in for an rw parameter, then binding of the signature fails, and the subroutine cannot be called with the constant argument.

If, on the other hand, you just want your own copy of the argument to work with inside the subroutine, and to leave the original untouched, then mark the parameter is copy.

Perl 6 makes you write more to do either of these, but the good news is that you probably won't need to often. One common pattern in some languages is to use rw style parameters when more than one result needs to be returned. In Perl 6, on the other hand, you're simply just allowed to return multiple values. Why not?

Passing Arrays, Hashes and Code

You have already seen how sigils can be used on variables to indicate how they can be used. In a signature, a variable with a sigil acts as a constraint on the type of argument that can be passed. Using the @ sigil, for example, checks that what is passed is capable of being iterated over. Failing to pass something that matches this constraint will cause the call to fail.

Similarly, using the % sigil implies that the caller must pass something that can be indexed into associatively, e.g. using <...> or {...}. There is also the & sigil, which requires that the caller pass something that itself may be called (for example, an anonymous subroutine). It also has the added benefit of allowing you to call it inside the subroutine body without having to put the & at the start.

A scalar (the $ sigil) does not imply any constraints at all, so anything may be bound to it, even if it could be bound to one of the other sigils too.

Optional Parameters

Sometimes there are parameters that can have a sensible default value, or are just not needed in every situation. In these cases, it is nice to mark such parameters as optional, so those calling the subroutine can choose whether or not they want to pass a value.

This can be done by assigning a default value in the signature, or by appending a question mark to the parameter name:

Named Paramters

When a subroutine has many parameters, it is sometimes hard to remember their respective order. When that happens, it is often easier to call them by name instead:

The names are just the ones that appeared as parameter names in the signature. When arguments are passed by name, the order in which they appear does not matter anymore.

When a parameter name in a signature is preceeded by a colon, then it can only be filled by name, not by position:

Named parameters are optional by default, adding a ! at the end makes it mandatory.

# TODO: example

Renaming Parameters

The name of named parameters is not tied to the variable name that is used inside the subroutine.

Parameters can also have multiple names. If the users are both British and Americans, one might write :color(:colour($c)) or :color(:$colour)).

Other Syntax for Calling by Name

Just like parameters can be written in the form :name($value), arguments can also be supplied with the so-called colon pair notation. So these three lines mean the same thing:

The parens after the name are optional. If they are omitted, the value is Bool::True. The shorthand for a value of Bool::False is :!name.

Other possible forms and their meanings are listed in the table below.

Shorthand           Long form                   Description

:allowed            allowed => Bool::True       Boolean flag
:!allowed           allowed => Bool::False      Boolean flag
:bev<tea coffee>    bev => ('tee', 'coffee')    List
:times[1, 3]        times => [1, 3]             Array
:hash{ a => 2}      hash => { a => 2}           Hash
:$var               var => $var                 Scalar variable
:@var               var => @var                 Array variable
:%var               var => %var                 Hash variable

All of these forms can also be used in other contexts too, where they construct Pair objects.

If you want to create a Pair object and pass that to a subroutine not by name, but by position, you can either put it in parenthesis (like (:$thing)), or you can use the => operator with a quoted string on the left-hand side: "thing" => $thing.

Slurpy Parameters

Returning Results

Subroutines can also return values.

The ASCII art examples from earlier would have been prettier if you used return values instead:

Instead of modifying a variable inside the subroutine, a string is returned, and used by the code that called the subroutine. Multiple values can also be returned:

The return is actually not necessary - the last statement that is run inside a subroutine is returned. So the example can be simplified to

However return has the additional effect of immediatly exiting the subroutine, so the following statements are not executed if the return statement is run:

# TODO: example

Working With Types

Basic Types

Adding Constraints

Captures

A signature can be viewed as a collection of parameters. Captures fill the same niche for arguments. Most of the time, you don't have to think about captures, just as you don't often think about a signature as a whole but instead focus more on the parameters in it. However, Perl 6 does provide you with a way to deal with captures directly, and occasionally it's useful to do so.

Captures have both a positional part and a named part, which act like a list and a hash respectively. From the point of view of making a call, the list-like part contains the positional parameters and the hash-like part contains the named parameters.

Creating And Using A Capture

Captures In Signatures

Unpacking

Sometimes you don't want to access access an array or hash as a whole, but rather extract some of the values. You could do that with ordinary accesses, or with signatures by doing another signature binding:

The signature binding approch might seem clumsy, but it can be incorporated into the main signature of the subroutine:

The brackets in the signature tell the compiler that a list-like parameter is expected, but instead of binding it to an array, it is unpacked into more parameters - here a single scalar, and an array containing the rest. This subsignature also acts as a constraint on the array parameter: the signature binding will fail unless the list in the capture contains at least one item.

Likewise a hash can be unpacked by using %(...) instead of the brackets, and named arguments inside them.

# TODO: come up with a good example

# TODO: generic object unpacking

Introspection

POD ERRORS

Hey! The above document had some coding errors, which are explained below:

Around line 1:

Unknown directive: =head0

Around line 101:

Non-ASCII character seen before =encoding in 'order-beer('Zlatý'. Assuming UTF-8