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<< : (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 : 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.
a >>
b
>>
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
|>
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:
pipeEx : Boolean
pipeEx = isSome (Optional.filter Nat.isEven (Some 5))
a <|
b
<|
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.
pipeBackwardsEx : Optional Nat
pipeBackwardsEx =
Optional.filter Nat.isEven <| pipeBackwardsEx.wrap 5
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.