The following operators are provided by the @unison/base library. They are commonly used for changing how a function is applied.
b << a
<< a<< : (b ->{𝕖} c) -> (a ->{𝕖} b) -> a ->{𝕖} c<<, also known as "compose," takes in two functions, b -> c and a -> b, and returns a function of type a -> c.
natToText : Nat -> Text
natToText = Boolean.toText << Nat.isEven
natToText 24⧨"true"Piping multiple functions together with the compose operator looks like:
textToBool : Text -> Boolean
textToBool = Boolean.not << Nat.isEven << Text.size
textToBool "hello"⧨trueThe expression is actually parsed:
((Boolean.not << Nat.isEven) << Text.size)Which gives us a function that expects Text.size's argument of Text and returns a Boolean
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.
Nat.isEven >> Boolean.notPiping together multiple >> calls looks like:
Text.size >> Nat.isEven >> Boolean.nota |> b
|> b|> : a -> (a ->{𝕖} b) ->{𝕖} bWhile >> returns a function, |> allows us to apply an argument to a function, returning the value of that function call. 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.
Some 2 |> Optional.filter Nat.isEven |> Optional.map (n -> n Nat.+ 1)⧨Some 3This version reads in the same order that things happen: start with Some 2, filter it, then map over the result. Compare that to the version without |>:
Optional.map (n -> n Nat.+ 1) (Optional.filter Nat.isEven (Some 2))⧨Some 3Here, the order in which the functions are applied is the same, but it may be harder to read: you start inside the parens and work out instead of left-to-right.
a <| b
<| b<| : (a ->{𝕖} b) -> a ->{𝕖} bWhen 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.