8.12

1.5 Optional and Keyword Arguments🔗ℹ

As we have seen, simple function definitions with fun have the function name, the parameters in parentheses, and the body after a colon:

fun avg(a, b):

  (a + b) / 2

> avg(-1, 5)

2

The fun form can be used in an expression position and without a function name, in which case it behaves like λ and produces an anonymous function value.

def curried_add:

  fun (x):

    fun (y):

      x+y

> curried_add(10)(20)

30

We’ve also seen that functions can have :: and :~ annotations on both the parameters and the return value. In general, the parameters can be bindings, which can express annotations and pattern matching. All of that works with fun in expression positions, too.

fun make_avg(a :: Number):

  fun (b :: Number) :: Number:

    (a + b) / 2

> make_avg(5)(-2)

3/2

> make_avg(5)("hello")

make_avg: argument does not satisfy annotation

  argument: "hello"

  annotation: Number

A function argument can be made optional by using = after the argument’s pattern and providing a default-value expression after =:

fun scale(Posn(x, y), factor = 1):

  Posn(factor * x, factor * y)

> scale(Posn(1, 2))

Posn(1, 2)

> scale(Posn(1, 2), 3)

Posn(3, 6)

When a function has multiple optional arguments, by-keyword arguments are especially useful. A keyword argument is indicated by prefixing a formal or actual argument with a shrubbery keyword, which is written with a leading ~, and then starting a block with :.

fun transform(Posn(x, y),

              ~scale: factor = 1,

              ~dx: dx = 0,

              ~dy: dy = 0):

  Posn(factor*x + dx, factor*y + dy)

> transform(Posn(1, 2))

Posn(1, 2)

> transform(Posn(1, 2), ~dx: 7)

Posn(8, 2)

> transform(Posn(1, 2), ~dx: 7, ~scale: 2)

Posn(9, 4)

Since a keyword by itself is not allowed as an expression or pattern, there is no possibility that a keyword will be inadvertently treated as an actual argument or binding pattern by itself. The #' prefix operator turns a keyword into an expression that produces the keyword, as in #'~scale. The operator also works on identifiers, as in #'x, to produce a symbol.

The keyword prefix and = for default values are not binding operators. They are specific to the syntax of fun.

If an argument name is the same as its keyword (just without the ~), then the : and argument name can be omitted. That only works for an argument that would otherwise be just an identifier and maybe a default value, because keywords don’t work as variable names in binding patterns.

fun transform(Posn(x, y),

              ~scale: factor = 1,

              ~dx = 0,

              ~dy = 0):

  Posn(factor*x + dx, factor*y + dy)

> transform(Posn(1, 2))

Posn(1, 2)

> transform(Posn(1, 2), ~dx: 7)

Posn(8, 2)

> transform(Posn(1, 2), ~dx: 7, ~scale: 2)

Posn(9, 4)

For functions that can take arbitrarily many arguments and for functions with multiple cases, see More Function Arguments.