The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Perl6::Overview::Subroutine - Subroutines

DESCRIPTION

Positional parameters

    sub foo ($bar, @baz, %grtz) {...}
    # Global (our()) subroutine &foo, taking a scalar, an array,
    # and a hash.
    foo(42,        @array,        %hash);          # works
    foo(bar => 42, baz => @array, grtz => %hash);  # works as well
    foo(:bar(42),  :baz(@array),  :grtz(%hash));   # ditto
    # Mixing named and positional arguments is allowed, too:
    foo(42, :baz(@array), :grtz(%hash));           # works

    Positional parameters are required by default.

Named parameters

    sub foo (:$named_only) {...}
    foo 42;                # illegal
    foo named_only => 42;  # ok
    foo :named_only(42);   # ok

Named parameters are optional be default.

Optional parameters

    sub foo ($bar?) {...}
    sub foo ($bar is optional) {...}
    foo();    # ok
    foo(42);  # ok

    sub foo (:$bar?) {...}  # allowed, but could be written as
    sub foo (:$bar)  {...}  # as named parameters are optional by
                            # default.

You can specify defaults:

    sub foo ($bar = 42) {...}
    foo();          # $bar is 42
    foo(17);        # $bar is 17

    sub foo (:$bar = 42) {...}
    foo();          # $bar is 42
    foo(:bar(17));  # $bar is 17

Defaults are calculated at runtime and can even refer to preceding parameters:

    sub foo ($bar, $baz = grmbl($bar)) {...}

Required parameters

    sub foo (:$bar!) {...}             # Required named parameter $bar
    sub foo (:$bar is required) {...}  # same

    sub foo ($bar!) {...}   # allowed, but could be written as
    sub foo ($bar)  {...}   # as positional parameters are required
                            # by default.

Slurpy parameters

Slurpy arrays slurp all remaining positional arguments:

    sub foo ($a, $b, *@rest) {...}
    foo 1,2,3,4,5;  # $a is 1, $b is 2, @rest is (3,4,5)
    foo 1,2;        # $a is 1, $b is 2, @rest is ()

Slurpy hashes slurp all remaining named arguments:

    sub foo ($a, $b, *%rest) {...}
    foo 1,2,3,4,5;  # error: "Too many positional arguments"
    foo 1,2, :foo<bar>;  # $a is 1, $b is 2, %rest is (foo => "bar")

"is rw"

By default, all parameters are read-only aliases:

    sub foo ($var) { $var = 42 };
    my $bar = 17; foo($bar);  # dies: "Cannot modify read-only variable"

    "is rw" causes *no* read-only proxy to be created:

    sub foo ($var is rw) { $var = 42 };
    my $bar = 17; foo($bar);  # works, $bar is 42 after the function call

"is copy"

"is copy" copies the variable, the original will remain unaffected:

    sub foo ($var is copy) { $var = 42 };
    my $bar = 17; foo($bar);  # works, but $bar unchanged

(This is the same as Perl 5's "my $var = shift" idiom.)

Note that "is rw" and "is copy" only refer to the first level of a structure:

    sub foo (@array) { @array[42] = 17 }
    foo @some_array;  # works, even though @array is not "is rw";
                      # @some_array[42] changed to 17

    sub foo (@array) { push @array, 17 }
    foo @some_array;  # does not work ("Cannot modify read-only
                      # variable")
            
    sub foo (@array is rw) { push @array, 17 }
    foo @some_array;  # does work, 17 appended to @some_array

Similarly, "is rw" and "is copy" may not do what you think they do on references:

    sub foo (Ref $ref) { $$ref = 17 }
    foo $some_ref;  # works, $$some_ref changed to 17

    sub foo (Ref $ref) { $ref = \$other_var }
    foo $some_ref;  # dies ("Cannot modify read-only variable"
            
    sub foo (Ref $ref is rw) { $ref = \$other_var }
    foo $some_ref;  # works, $some_ref changed to \$other_var

Re-binding parameters

The following code is legal and does not die:

    sub foo ($var) { $var := $some_other_var; ... }
    foo 42;
    foo $some_var;

But the original containers are not modified in any way, neither 42 nor $some_var get rebound, only &foo's $var does no longer refer to 42 or $some_var, but to $some_other_var.

[XXX: Unpacking arrays and hashes, pattern matching, specifying parameter types]

Specifying the scope of a declaration

    sub foo {...}
    # our() subroutine, may be called before the declaration:
    foo(...); sub foo {...}  # legal
    # (Note that this is sugar for
    #   BEGIN { our &foo := sub (...) {...} })

    my sub foo {...}
    # Lexical (my()) subroutine, may not be called before the declaration.
    # Only visible in the scope in which it was declared:
    { my sub foo {...}; foo(...) }  # legal
    { foo()                      }  # illegal
                                    # ("Can't find subroutine &foo")

    our sub foo {...}
    # our() subroutine, may not be called before the declaration.

    sub *foo {...}
    # Really global subroutine, visible in all scopes.

"is rw" on a subroutine

    "is rw" applied to a subroutine causes *no* read-only proxy to be
    created around the return value of a subroutine:

    sub foo { $some_var }
    foo() = 42;  # error: "Cannot modify read-only variable"

    sub foo is rw { $some_var }
    foo() = 42;  # works, $some_var set to 42

    sub foo is rw { 17 }
    foo() = 42;  # still does not work -- 17 is a constant
    # Instead use:
    sub foo is rw { my $var = 17 }
    foo() = 42;  # works

The "Proxy" class

Following the "assignments should look like assignments" rule, usage of "is rw" for accessor-like subroutines is encouraged. You can use the "Proxy" class to validate input:

    sub foo () is rw {
        return new Proxy:
            FETCH => { ...code to return on get... },
            STORE => -> $new { ...code to run on assignment... };
    }
    say foo();
    foo() = 42;

Note that assigning a proxy to a variable with = will loose the proxy's magicalness:

    my $var = foo();        # invokes FETCH on foo()
    $var = $invalid_input;  # works; STORE not called

You have to use binding (:=) instead:

    my $var := foo();
    $var = $invalid_input;  # STORE called

Calling subroutines

Whitespace matters:

    foo 42;           # calls &foo, one positional argument (number 42)
    foo(42);          # same, the parens are sub call parens (postfix .())
    foo (42);         # same, but the parens are grouping parens

    foo 42, 23;       # two positional arguments (42 and 23)
    foo(42, 23);      # same

    foo (42, 23);     # one positional argument (the array (42, 23))

Parentheses matter:

    foo bar => 42;    # one named argument (42)
    foo :bar(42);     # same
    foo(bar => 42);   # same, the parens are grouping parens
    foo(:bar(42));    # same, the parens are grouping parens

    foo (bar => 42);  # one positional argument (the pair (bar => 42))
    foo (:bar(42));   # same, the parens mark the pair to be a Pair object,
                      # not a named argument

Splatting:

    foo $pair;        # one positional argument ($pair)
    foo [,] $pair;       # one named argument
                      # ($pair.value passed by the name $pair.key)

    foo @array;       # one positional argument (the array @array)
    foo [,] @array;      # many positional arguments
                      # (@array's contents are passed positionally)

    foo %hash;        # one positional argument (the hash %hash)
    foo [,] %hash;       # many named arguments
                      # (%hash.values are passed by the names %hash.keys)