11

Say I want to declare a function whose parameter is an array of strings:

sub process-string-array(Str[] stringArray) # invalid
{
  ...
}

How would I do that ?

zentrunix
  • 2,098
  • 12
  • 20

1 Answers1

11

It depends on the sigil you want to use:

sub process-string-array(Str        @array) { ... }  # @-sigil
sub process-string-array(Array[Str] $array) { ... }  # $-sigil

Note that you have to be careful to pass in a declared Str array to do this which means adhoc arrays will need to passed in with a typed declaration:

my Str @typed-array = <a b c>;
process-string-array <a b c>;                 # errors
process-string-array @typed-array;            # typed array in
process-string-array Array[Str].new: <a b c>; # adhoc typed array

If you don't want to deal with typing arrays like this, you can use a where clause to accept any Any-typed array that happens to include only Str elements (which is often easier to use IME):

sub process-string-array(@array where .all ~~ Str) { ... }

This, however, (as jnthn reminds in the comments) requires type checking each element (so O(n) perf versus O(1) ), so depending on how performance sensitive things are, it may be worth the extra code noise. Per Brad's suggestion, you could multi it, to speed things up when the array is typed and fallback to the slower method when not.

multi sub process-string-array(Int @array) { 
    ...  # actual processing here
}
multi sub process-string-array(@array where .all ~~ Str) { 
    process-string-array Array[Int].new: @array
}
user0721090601
  • 5,276
  • 24
  • 41
  • I had tried the first option `sub name(Str @array)` but had errors, because I was passing an untyped array, just like your warning says! As to the `where` clause, I had imagined something like your example would work, but hadn't tried because I wanted "actual" typed array declarations. Thanks. – zentrunix Dec 31 '20 at 16:22
  • 4
    Note that checking for a typed array is `O(1)` in the size of the array, but the `where` approach is `O(n)` - that is, you really don't want to do it anywhere performance matters. – Jonathan Worthington Dec 31 '20 at 17:16
  • 1
    @JonathanWorthington Thanks for the reminder: I normally try to mention the trade offs but totally forgot in this case (I normally use the where in slurpy constructions where the typed array isn't possible. (Although I haven't tested yet to see if vrurg's new coercion system will allow for `:( Int() @foo)` – user0721090601 Dec 31 '20 at 19:28
  • 3
    You could use multi subs. `multi process-string-array(@array where .all ~~ Str) { samewith( Array[Str].new: @array )}` `multi process-string-array(Str @array) { ... }` – Brad Gilbert Dec 31 '20 at 21:40
  • @user0721090601 does `grep` do as well as `where` in this situation, i.e. `sub process-string-array(@array.grep(not *.Int)) { ... }` ? In any case, I seem to run into "IntStr" issues since something like `my @a = ` tested with `put @a.grep(*.Str)` returns `a b c 1 2 3` . Any insight appreciated. – jubilatious1 Jan 01 '21 at 18:21
  • 1
    @jubilatious1: `grep` will select the elements that are `Int` and through out there others (`where` doesn't modify the array, if it finds a non-`Int`, it will prevent the method from being called). In your example, `@a` is an `Array[Any]` and `@a.grep(*.Str)` is a `Seq`, neither would pass for a `Array[Str]`. – user0721090601 Jan 01 '21 at 23:20
  • @BradGilbert 's suggestion is neat – zentrunix Jan 06 '21 at 11:49