The Conditional Choice Operator
by Nicolas Wu
Posted on 1 August 2011
Tags: Haskell
A recent reddit post asking for a library of conditional one-liners and combinators reminded me of one of my favourite operators: the conditional choice. Most programmers are used to the so-called McCarthy conditional:
if p then x else y
An alternative way of viewing this is as a ternary operator:
<| p |> y x
This operator is known as the conditional choice, but I’m told that it was introduced by Tony Hoare, so maybe naming it the Hoare conditional would make more sense (it makes an appearance in Hoare’s work on Communicating Sequential Processes and Unifying Theories of Programming, but I haven’t found any references to its original introduction).
Laws
Rendering conditonals as ternary operators makes it clear that there are a number of nice properties that hold true:
Idempotency
< x <| p |> x == x
Left-Identity
< x <| True |> y == x
Right-Identity
< x <| False |> y == y
Left-Distributivity
< x <| p |> (y <| q |> z) == (x <| p |> y) <| q |> (x <| p |> z)
Right-Distributivity
< (x <| p |> y) <| q |> z == (x <| q |> z) <| p |> (y <| q |> z)
Symmetry
< x <| p |> y == y <| not p |> x
Conjunction-Associativity
< (x <| p |> y) <| q |> z == x <| p && q |> (y <| q |> z)
Disjunction-Associativity
< x <| p |> (y <| q |> z) == (x <| p |> y) <| p || q |> z
Conjunction-Collapse
< x <| p |> (y <| p && q |> z) == x <| p |> z
Disjunction-Collapse
< (x <| p || q |> y) <| q |> z == x <| q |> z
Abiding (Interchange)
< x # y <| p |> v # w == (x <| p |> v) # (y <| p |> w)
These laws are easily proved by considering the cases where p
and q
are
True
and False
.
Implementation
In Haskell, implementing this operator is quite simple.
First we’ll define the right bracket, which takes a predicate
and a value x
, and returns Nothing
if the predicate is True
,
and returns Just x
when it is False
:
(|>) :: Bool -> a -> Maybe a
True |> _ = Nothing
False |> y = Just y
The left bracket is equivalent to fromMaybe
, where the resulting value
from the application of the right bracket (which evaluates the predicate) is
consumed. If the result was Nothing
, then we use the value x
, otherwise
we have Just y
, and return y
as the result.
(<|) :: a -> Maybe a -> a
<| Nothing = x
x <| Just y = y _
Finally we give the operators low infixity precedence, and make them right associative:
infixr 0 <|
infixr 0 |>
Defining the operator this way makes the ternary operator right associative, so that:
<| p |> y <| q |> z == x <| p |> (y <| q |> z) x
Right associativity here is useful so that reading from left to right, the result is the expression to the left of the first predicate that is true.