Computation expressions
Computation expressions streamlines some types of repetitive code, similar to do notation in Haskell or computation expressions in F#.
Begin code:
module compexp
import novah.computation
// without computation expressions
uglyFindUser : String -> Result User String
uglyFindUser name =
case fetchUser name of
Ok user ->
case fetchAddress user of
Ok address -> Ok { address | user }
Err err -> Err err
Err err -> Err err
// using computation expressions
prettyFindUser : String -> Result User String
prettyFindUser name = do.result
let! user = fetchUser name
let! address = fetchAddress user
return { address | user }
// all permutations of x and y where x and y are not equals
uglyPairs : List (Tuple Int Int)
uglyPairs =
[1 .. 10] |> List.flatMap \x ->
[1 .. 10] |> List.mapSome \y ->
if x != y then
Some (x ; y)
else
None
// same but with computation expressions
prettyPairs : List (Tuple Int Int)
prettyPairs = do.list
for x in [1 .. 10] do
for y in [1 .. 10] do
if x != y then yield x ; y
End code.
Defining your own
Computation expressions are just a syntactic sugar for direct function calls. Defining your own is pretty simple and only requires that you choose which functions to support.
Function name | Typical type | Description |
---|---|---|
bind | m a -> (a -> m b) -> m b | Defines the let bang (let!) construct |
for | m a -> (a -> m b) -> m b | Defines the for construct which works the same as bind. Only one of for or bind should be implemented. |
return | a -> m a | Defines the return construct |
yield | a -> m a | Defines the yield construct which works the same as return. Only one of return or yield should be implemented. |
zero | m a | `zero` will be used for `if`s without an else and expressions that do not `return` or `yield` |
combine | m a -> m a -> m a | `combine` will be used to join line-separated expressions |
Begin code:
module compexp
// define a computation expression for lists
pub
list :
{ "for" : List a -> (a -> List b) -> List b
, "yield" : a -> List a
, combine : List a -> List a -> List a
, zero : List a
}
list =
{ "for": flip List.flatMap
, "yield": \x -> [x]
, combine: \(l1 : List a) l2 -> l1 ++ l2
, zero: []
}
// using the computation expression
squares : List Int
squares = do.list
for x in [1 .. 10] do yield x * x
End code.