8.15.0.6

2.8 Function Shorthand and Pipelines🔗ℹ

Functions are values, and they can be passed to other functions. For example, the List.find function (which can be called equivalently as a find method on a list) takes a predicate as a function to determine which value to return, the List.map function (or map method) takes a function to apply to each element, and List.sort (or sort method), takes a comparison function to determine when one element should be before another.The List.map method is about mapping a function over a list to produce a new list, and not about the Map datatype.

> List.find([1, 3, 2], fun (x): x > 2)

3

> [1, 3, 2].find(fun (x): x > 2)

3

> [1, 3, 2].map(fun (x): x + 1)

[2, 4, 3]

> [1, 3, 2].sort(fun (x, y): x > y)

[3, 2, 1]

We cannot pass > directly to sort, because the operator > is not an expression by itself. Simple functions that use operators, however, can be written with a shorthand of _ within ():

> List.find([1, 3, 2], (_ > 2))

3

> [1, 3, 2].find((_ > 2))

3

> [1, 3, 2].map((_ + 1))

[2, 4, 3]

> [1, 3, 2].sort((_ > _))

[3, 2, 1]

Each immediate _ in a () expression is turned into a distinct argument for an anonymous function that replaces the parenthesized expression. “Immediate” here means that the _ is not nested more deeply within the (), such as being in a block or under more parentheses. As the last example above illustrates, () plus _ is a general way to turn on operator into a function.

Another form of the _ shorthand is like a function call, but where at least one argument in the call is exactly _ (not combined with other terms). Similar to the conversion for (), each separate _ becomes an argument to the function.

> [1, 3, 2].map(math.max(_, 2))

[2, 3, 2]

> [[1, 3, 2], [6, 0, 5]].map(List.sort(_, fun (x, y): x > y))

[[3, 2, 1], [6, 5, 0]]

> [[1, 3, 2], [6, 0, 5]].map(List.sort(_, (_ > _)))

[[3, 2, 1], [6, 5, 0]]

The _ shorthand notations are designed to be relatively unlikely to collide with an expression form, because _ by itself as an expression is a syntax error. For example, math.max(_, 2) by itself is a syntax error. (Also, _ as a binding form matches without binding a name, so _ is not easy to bind locally. The shorthand is limited, however, and intended only for the simplest of cases. Fall back to fun at the first sign of trouble.

> [1, 3, 2].map(2 * (_ + 1))

*: value does not satisfy annotation

  annotation: Number

  value: #<function:...ion-shorthand.scrbl:67:22>

> [1, 3, 2].map(fun (x): 2 * (x + 1))

[4, 8, 6]

Function shorthands with _ can be particularly useful with the |> operator, which takes an argument and a function and calls the function on the argument. The |> operator is left-associative with low precedence, so it can form pipelines of function calls.

> -1 |> math.abs

1

> -1 |> math.abs |> (_ * 2)

2

> [3, 1, 2]

    |> List.sort(_)

    |> ([0] ++ _ ++ [100])

    |> (_.map(to_string))

["0", "1", "2", "3", "100"]