2.7 More Function Arguments
As we saw in Optional and Keyword Arguments, by using optional arguments and | clauses, you can define a function that accepts a varying number of arguments. When using only those features, however, the allowed number of arguments is still a fixed set of small numbers. To define or call a function that accepts any number of arguments, use the ... repetition form or & list-splicing form after other arguments.
For example, in the following definition of add, the argument x is bound as a repetition, which allows any number of arguments:
> add(1, 2, 3, 4)
10
As illustrated in the calls to add, ... and & work for function calls the same way that they work for constructors specifically. Elements of the repetition or list are spliced into the function call as separate arguments, as opposed to being passed as a list. While only one of ... or & can be used in a function definition, they can appear any number of times and in any order within a function call.
A function doesn’t have to accept an arbitrary number of arguments for ... or & to work in a call to the function, as long at the total number of spliced arguments matches the number that the function expects.
1024
The add function could also be written with & for its argument instead of ..., like this:
> add(1, 2, 3, 4)
10
Note that the annotation on x as a repetition refers to an individual argument within the repetition, while the annotation on xs refers to the whole list of arguments.
To create a function that works with any number of keyword arguments, use ~& to bind an argument that receives all additional keyword arguments. The additional arguments are collected into a map with keywords as keys.
> roster(~pitcher: "Dave", ~manager: "Phil", ~catcher: "Johnny")
{#'~catcher: "Johnny", #'~pitcher: "Dave"}
Similarly, use ~& in a function call to pass keyword arguments that are in map. Using ~& to call a function is most useful when chaining from one keyword-accepting function to another.
| shape_area(~type: "circle", ~& props): circle_area(~& props)
| shape_area(~type: "rectangle", ~& props): rectangle_area(~& props)
> shape_area(~type: "circle", ~radius: 1)
3.14
> shape_area(~type: "circle", ~diameter: 8.5)
113.4325
> shape_area(~type: "rectangle", ~width: 8.5, ~height: 11)
93.5
A function call can use ~& any number of times, and in any order compared to other arguments. A function definition can use ~& at most once, and only after all other arguments other than a & argument or ... repetition argument. A ~& for a function definition can appear in either order with a & or ... argument.
Functions can use | cases, annotations, and/or pattern matching to distinguish calls with the same number of arguments. Different cases use &, ..., and ~& independently.
(n + add(& ns)) / (1 + List.length(ns))
Posn(avg(p.x, & Function.map(Posn.x, ps)),
avg(p.y, & Function.map(Posn.y, ps)))
> avg(1, 2, 6)
3
> avg(Posn(0, 0), Posn(1, 3), Posn(2, 0))
Posn(1, 1)