This article provides an overview of monadic programming in Ela.
Ela like other functional languages does provide a natural support for monadic programming through lambda. However,
Ela provides additional support for monads through both library and language. It includes flexible monadic abstractions
(exposed through Ela classes) and special syntax support that can simplify monadic programming (a so called
The high level of an approach to monadic programming is pretty similar to Haskell; syntax used in
effectively mimics the one used in Haskell. The monadic class hierarchy, however, is rather different.
All monadic classes are currently are not exposed through prelude module, but are included in a dedicated
module. This module also defines several useful functions, including the well known "bind" function. In order to write
monadic code you first need to learn how to use the following three basic functions.
The fist function is
(so called "bind"):
m >>= f
The semantics of this function is pretty trivial - it takes the contents of its first argument (which should be a monad)
and passes it to a second argument (which should be a function) as a parameter.
This function obeys the following law which can also help to understand its mechanics:
return a >>= k == k a
The code above, if evaluated, would return
. This means that the two expressions are equivalent and would always
fetch the same value.
, used in our code sample, is the second basic function - it just takes a value and puts it in a
return 2 ::: List
As you can see this function should be always executed in a context of a monadic type (and linked lists in Ela are
monads). In fact the name
is just an alias for a function
and is defined in
return = point
The third function is actually a redundant one. This a a
function (Haskell equivalent is
). It can be defined
in terms of
function like so:
xs >>- ys = xs >>= (\_ -> ys)
(And this is in fact the way how this function is actually defined in
Behavior of this function is pretty similar to the standard
function - it takes the contents of the first argument
and simply ignores it, moving on to the second argument.
monad defines the following core monadic classes:
Pointed (there are other classes
but these three are the most important). Class
Functor contains a single
fmap function which is a generalization
map on lists. Class
Union contains a single
join function which is a generalization of
concat on lists.
Pointed contains a single
point function which puts a value into a monadic contexts (or, in a case of
lists, "packs" a value in a list). For clarity - this is how these classes are implemented for linked lists:
instance Functor List where
fmap = List.map
instance Union List where
join = List.concat
instance Pointed List where
point x = [x]
In fact function "bind" discusses in a previous section is not a class function (like in Haskell, where this function
is a member of
Monad class), but is defined through
join like so:
xs >>= f = join (fmap f xs)
return, as it was already mentioned, is simply an alias for
A so called
notation is a special syntax that was added to Ela to simplify writing monadic code. Heavy usage of
"bind" operator can lead to a pretty unreadable code; the
notation can help to remove the clutter.
notation is a special type of an expression that can be used in all the contexts where expression is valid. It
keyword. The layout for
expression is pretty similar to the one used for
binding - the first
entry can be on the same line with
, but other entries should indented after
foo = do
x <- Some "Is it "
y <- Some 42
z <- Some '?'
Some (x ++ show y ++ z)
The monadic notation in Ela mimics the one used in Haskell.
It is also possible to use
notation in the following way:
putStrLn "What is your height?"
x <- readAny
putStrLn "What is your weight?"
y <- readAny
let rec = x / y
if rec > 2.25 then "You're too thin!"
else if rec < 2 then "You're too heavy!"
else "You're OK...")
The example above is written using
IO monad and console IO actions available in
Pattern matching in do-notation
It is possible to pattern match in bindings in do-notation as in regular bindings. For example, consider the following function:
res s = do
(x::_) <- Some s
We can call it like so:
res "Hello" ::: Maybe
We can, hovewer, provide an empty string as an argument as well:
res "" ::: Maybe
When we pass an empty string to a
res function, a pattern match
(x::_) fails. But instead of raising an exception it calls a
function which is a member of
class Failure a where
Maybe implements this class like so:
instance Failure Maybe where
failure _ = None
As a result a function
res above returns
None instead of an error.