syntax multi

Documentation for syntax multi assembled from the following types:

language documentation Functions

From Functions

(Functions) declarator multi multi

Raku allows for writing several routines with the same name but different signatures. When the routine is called by name, the runtime environment determines the proper candidate and invokes it.

Each candidate is declared with the multi keyword. Dispatch happens depending on the parameter arity (number), type and name; and under some circumstances the order of the multi declarations. Consider the following example:

# version 1 
multi happy-birthday$name ) {
    say "Happy Birthday $name !";
}
 
# version 2 
multi happy-birthday$name$age ) {
    say "Happy {$age}th Birthday $name !";
}
 
# version 3 
multi happy-birthday:$name:$age:$title  = 'Mr' ) {
    say "Happy {$age}th Birthday $title $name !";
}
 
 
# calls version 1 (arity) 
happy-birthday 'Larry';                        # OUTPUT: «Happy Birthday Larry !␤» 
# calls version 2 (arity) 
happy-birthday 'Luca'40;                     # OUTPUT: «Happy 40th Birthday Luca !␤» 
# calls version 3 
# (named arguments win against arity) 
happy-birthdayage => '50'name => 'John' ); # OUTPUT: «Happy 50th Birthday Mr John !␤» 
# calls version 2 (arity) 
happy-birthday'Jack'25 );                  # OUTPUT: «Happy 25th Birthday Jack !␤» 
 

The first two versions of the happy-birthday sub differs only in the arity (number of arguments), while the third version uses named arguments and is chosen only when named arguments are used, even if the arity is the same of another multi candidate.

When two sub have the same arity, the type of the arguments drive the dispatch; when there are named arguments they drive the dispatch even when their type is the same as another candidate:

multi happy-birthdayStr $nameInt $age ) {
    say "Happy {$age}th Birthday $name !";
}
 
multi happy-birthdayStr $nameStr $title ) {
    say "Happy Birthday $title $name !";
}
 
multi happy-birthdayStr :$nameInt :$age ) {
    say "Happy Birthday $name, you turned $age !";
}
 
happy-birthday 'Luca'40;                 # OUTPUT: «Happy 40th Birthday Luca !␤» 
happy-birthday 'Luca''Mr';               # OUTPUT: «Happy Birthday Mr Luca !␤» 
happy-birthday age => 40name => 'Luca';  # OUTPUT: «Happy Birthday Luca, you turned 40 !␤» 
 

Named parameters participate in the dispatch even if they are not provided in the call. Therefore a multi candidate with named parameters will be given precedence.

For more information about type constraints see the documentation for the Signature class.

multi as-json(Bool $d{ $d ?? 'true' !! 'false'}
multi as-json(Real $d{ ~$d }
multi as-json(@d)      { sprintf '[%s]'@d.map(&as-json).join(''}
 
say as-jsonTrue );                        # OUTPUT: «true␤» 
say as-json10.3 );                        # OUTPUT: «10.3␤» 
say as-json( [ True10.3False24 ] );   # OUTPUT: «[true, 10.3, false, 24]␤»

For some signature differences (notably when using a where clause or a subset) the order of definition of the multi methods or subs is used, evaluating each possibility in turn. See multi resolution by order of definition below for examples.

multi without any specific routine type always defaults to a sub, but you can use it on methods as well. The candidates are all the multi methods of the object:

class Congrats {
    multi method congratulate($reason$name{
        say "Hooray for your $reason$name";
    }
}
 
role BirthdayCongrats {
    multi method congratulate('birthday'$name{
        say "Happy birthday, $name";
    }
    multi method congratulate('birthday'$name$age{
        say "Happy {$age}th birthday, $name";
    }
}
 
my $congrats = Congrats.new does BirthdayCongrats;
 
$congrats.congratulate('promotion','Cindy'); # OUTPUT: «Hooray for your promotion, Cindy␤» 
$congrats.congratulate('birthday','Bob');    # OUTPUT: «Happy birthday, Bob␤»

Unlike sub, if you use named parameters with multi methods, the parameters must be required parameters to behave as expected.

Please note that a non-multi sub or operator will hide multi candidates of the same name in any parent scope or child scope. The same is true for imported non-multi candidates.

Multi-dispatch can also work on parameter traits, with routines with is rw parameters having a higher priority than those that do not:

proto þoo (|) {*}
multi sub þoo$ðar is rw ) { $ðar = 42 }
multi sub þoo$ðar ) { $ðar + 42 }
my $bar = 7;
say þoo($bar); # OUTPUT: «42» 

proto

proto is a way to formally declare commonalities between multi candidates. It acts as a wrapper that can validate but not modify arguments. Consider this basic example:

proto congratulate(Str $reasonStr $name|{*}
multi congratulate($reason$name{
   say "Hooray for your $reason$name";
}
multi congratulate($reason$nameInt $rank{
   say "Hooray for your $reason$name -- got rank $rank!";
}
 
congratulate('being a cool number''Fred');     # OK 
congratulate('being a cool number''Fred'42); # OK
congratulate('being a cool number'42);         # Proto match error 

The proto insists that all multi congratulate subs conform to the basic signature of two strings, optionally followed by further parameters. The | is an un-named Capture parameter, and allows a multi to take additional arguments. The first two calls succeed, but the third fails (at compile time) because 42 doesn't match Str.

say &congratulate.signature # OUTPUT: «(Str $reason, Str $name, | is raw)␤» 

You can give the proto a function body, and place the {*} (note there is no whitespace inside the curly braces) where you want the dispatch to be done. This can be useful when you have a "hole" in your routine that gives it different behavior depending on the arguments given:

# attempts to notify someone -- False if unsuccessful 
proto notify(Str $userStr $msg{
   my \hour = DateTime.now.hour;
   if 8 < hour < 22 {
      return {*};
   } else {
      # we can't notify someone when they might be sleeping 
      return False;
   }
}

Since proto is a wrapper for multi candidates, the signatures of the routine's multi candidates do not necessarily have to match that of the proto; arguments of multi candidates may have subtypes of those of the proto, and the return types of the multi candidates may be entirely different from that of the proto. Using differing types like this is especially useful when giving proto a function body:

enum DebugType <LOG WARNING ERROR>;
 
#|[ Prints a message to stderr with a color-coded key. ] 
proto debug(DebugType:D $typeStr:D $message --> Bool:_{
    note sprintf qb/\e[1;%dm[%s]\e[0m %s/{*}$type.key$message
}
multi debug(LOG;; Str:D --> 32)     { }
multi debug(WARNING;; Str:D --> 33{ }
multi debug(ERROR;; Str:D --> 31)   { }

{*} always dispatches to candidates with the parameters it's called with. Parameter defaults and type coercions will work but are not passed on.

proto mistake-proto(Str() $strInt $number = 42{*}
multi mistake-proto($str$number{ say $str.^name }
mistake-proto(742);  # OUTPUT: «Int␤» -- not passed on 
mistake-proto('test'); # fails -- not passed on 

A longer example using proto for methods shows how to extract common functionality into a proto method.

class NewClass {
    has $.debug is rw = False;
    has $.value is rw = 'Initial value';
    proto method handle| ) {
        note "before value is 「$.value" if $.debug;
        {*}
        note "after value is 「$.value" if $.debug;
    }
    multi method handle(Str $s{
        $.value = $s;
        say 'in string'
    }
    multi method handle(Positional $s{
        $.value = $s[0];
        say 'in positional'
    }
    multi method handle$a$b ) {
        $.value = "$a is looking askance at $b";
        say 'with more than one value'
    }
}
my NewClass $x .= new;
$x.handle('hello world');
$x.handle(<hello world>);
$x.debug = True;
$x.handle('hello world');
$x.handle(<hello world>);
$x.handle('Claire''John');
# OUTPUT: 
# in string 
# in positional 
# before value is 「hello」 
# in string 
# after value is 「hello world」 
# before value is 「hello world」 
# in positional 
# after value is 「hello」 
# before value is 「hello」 
# with more than one value 
# after value is 「Claire is looking askance at John」 

only

The only keyword preceding sub or method indicates that it will be the only function with that name that inhabits a given namespace.

only sub you () {"Can make all the world seem right"};

This will make other declarations in the same namespace, such as

sub you ( $can ) { "Make the darkness bright" }

fail with an exception of type X::Redeclaration. only is the default value for all subs; in the case above, not declaring the first subroutine as only will yield exactly the same error; however, nothing prevents future developers from declaring a proto and preceding the names with multi. Using only before a routine is a defensive programming feature that declares the intention of not having routines with the same name declared in the same namespace in the future.

(exit code 1)
===SORRY!=== Error while compiling /tmp/only-redeclaration.p6
Redeclaration of routine 'you' (did you mean to declare a multi-sub?)
at /tmp/only-redeclaration.p6:3
------> <BOL><EOL>

Anonymous subs cannot be declared only. only sub {} will throw an error of type, surprisingly, X::Anon::Multi.

multi resolution by order of definition

When the breakdown by parameter type is not enough to find an unambiguous match, there are some different tie breakers that may be evaluated in order of declaration of the methods or subs: these include where clauses and subsets, named parameters, and signature unpacks.

In this code example, two multi subs are distinguished only by where clauses where there's one ambiguous case that might pass either of them, the value 4. In this case, which ever multi sub is defined first wins:

{
  multi sub check_range ( Int $n where {$_ > 3} ) {
      return "over 3";
  };
  multi sub check_range ( Int $n where {$_ < 5} ) {
      return "under 5";
  };
  say check_range(4);  # OUTPUT: over 3 
}
{
  multi sub check_range ( Int $n where {$_ < 5} ) {
      return "under 5";
  };
  multi sub check_range ( Int $n where {$_ > 3} ) {
      return "over 3";
  };
  say check_range(4);  # OUTPUT: under 5 
}

In the following example, three subsets are used to restrict strings to certain allowed values, where there are overlaps between all three:

subset Monster  of Str where { $_ eq any( <godzilla gammera ghidora> ) };
subset Hero     of Str where { $_ eq any( <godzilla ultraman inframan> ) };
subset Knockoff of Str where { $_ eq any( <gammera inframan thorndike> ) };
 
{
    multi sub speak (Monster $name{
        say "The monster, $name roars!";
    }
    multi sub speak (Hero $name{
        say "The hero, $name shouts!";
    }
    multi sub speak (Knockoff $name{
        say "The knockoff, $name, slinks away...";
    }
    speak('godzilla');     # OUTPUT: The monster, godzilla roars! 
    speak('gammera');      # OUTPUT: The monster, gammera roars! 
    speak('inframan');     # OUTPUT: The hero, inframan shouts! 
}

Note that here 'godzilla' is treated as Monster, not as Hero, because the Monster multi comes first; and neither 'gammera' or 'inframan' are treated as Knockoff, because that multi comes last.

It should be noted that the order of definition is the order in which Raku sees them, which might not be easy to discern if, for example, the multi subs were imported from different modules. As the organization of a code base becomes more complex, object classes may scale better than using subsets as types, as in this example.