Operators for function application

When working with Unison code, you might encounter the following operators which are provided by the @unison/base library. They are commonly used for manipulating the order in which a function is applied to its arguments.

a << b

<< : (b ->{๐•–} c) -> (a ->{๐•–} b) -> a ->{๐•–} c

<<, also known as "compose," takes in two functions and returns a new function that applies the rightmost function to its argument and then applies the leftmost function to the result. In other words, it takes a function of type b -> c and a function of type a -> b and returns a function of type a -> c.

composeEx : Nat -> Boolean
composeEx = Boolean.not << Nat.isEven

Piping multiple functions together with the compose operator looks like:

composeMultiEx : Text -> Boolean
composeMultiEx = Boolean.not << Nat.isEven << Text.size

The expression is actually parsed:

((Boolean.not << Nat.isEven) << Text.size)

Giving us a function that expects Text.size's argument of Text and returning the final result of applying the Boolean.not function.

๐Ÿ“Œ
Repeated infix operator application is generally grouped with the leftmost expressions happening first. In other words, they are left-associative. Read more about this in the language guide section about operators and function application

a >> b

>> : (a ->{๐•–} b) -> (b ->{๐•–} c) -> a ->{๐•–} c

>>, also known as andThen, is like compose with the function argument order reversed. It takes in two functions a->b and b->c and returns a function from a->c.

andThenEx : Boolean
andThenEx = (>>) Nat.isEven Boolean.not 4

Piping together multiple >> calls looks like:

andThenMultiEx : Boolean
andThenMultiEx = (>>) (Text.size >> Nat.isEven) Boolean.not "Boo"

a |> b

|> : a -> (a ->{๐•–} b) ->{๐•–} b

While >> returns a function, |> allows us to apply an argument to a function, returning the value of that function call. When using |> it's helpful to remember that the function argument should be on the left hand side of the operator. The value of this operator might not be immediately obviousโ€”after all, why use special operators for function application when parentheses will suffice ๐Ÿค”โ€”but you'll often see multiple instances of the |> operator chained together to form a complete expression.

pipeRightEx : Optional Nat
pipeRightEx =
  use Nat +
  Some 5 |> Optional.filter Nat.isEven |> Optional.map (n -> n + 1)

You might find that there's a greater fit between the semantic order of this expression and the order in which the function calls are applied than in the non |> version of this expression:

๐Ÿง 

When chaining the |> operator in a multi-line expression, if the operator is the first thing on a new line, it should be indented.

List.range 0 10
   |> map (pow 2)
   |> sum

If the operator is the last thing on the line, the following expression does not need to be indented.

range 0 10 |>
map (pow 2) |>
sum

a <| b

<| : (a ->{๐•–} b) -> a ->{๐•–} b

When using <|, the function should be on the left hand side of the operator with the function's argument on the right hand side. You can read the <| operator as "pipe backwards." You might choose to use this operator when the argument to the function in question needs some "pre-processing" which would otherwise involve parentheses.

Note that the <| operator does not change the precedence of the operator function calls. The leftmost sub-expression is still executed first. You can use parentheses to change the grouping.