Implementing Cond in Elm
I’'m a great fan of the case
statement, but there are some times when I miss the cond
function found in Lisp, Racket, etc. This occasionally comes up when there is a series of conditions that have to be checked, which of course you can do with a nested if-then-else
construct. But that leads, in my experience, to fragile code that can be hard to read and maintain. Besides, as a new and enthusiastic user of elm-review
, I am trying to reduce the cyclomatic complexity of my code, and nested if-then-elses
can boost it way up.
A good solution is to use cond
, but Elm doesn’t have this. No big deal — as shown below, it is easy enough to hack a simple version together using Step
and loop
, which are also user-defined.
Note that if a condition is satisfied, no further conditions will be evaluated.
Example.
> conditions = [ \x -> x >= 0 && x < 1
, \x -> x >= 1 && x < 2
, \x -> x >= 2 && x < 3 ]
> outputs = ["red", "green", "blue"]
> cond conditions outputs 1.5
Just "green"
Code.
cond : List (a -> Bool) -> List b -> a -> Maybe b
cond conditions outputs input =
loop { conditions = conditions
, outputs = outputs
, input = input} nextStep
type alias State a b = { conditions : List (a -> Bool)
, outputs : List b
, input : a}
nextStep : (State a b) -> Step (State a b) (Maybe b)
nextStep state =
case (List.head state.conditions, List.head state.outputs) of
(Nothing, _) -> Done Nothing
(_, Nothing) -> Done Nothing
(Just condition, Just output) -> if condition state.input
then Done (Just output)
else Loop {state | conditions = List.drop 1 state.conditions
, outputs = List.drop 1 state.outputs}
type Step state x
= Loop state
| Done x
loop : state -> (state -> Step state a) -> a
loop s f =
case f s of
Loop s_ ->
loop s_ f
Done b ->
b