The term “Monad” comes from mathematics or philosophy, which exact meaning is irrelevant to programming in haskell.
Here’s the synopsis, but you really don’t need to read it:
A monad is a special case of a monoid.
A monoid is a mathematical structure: Set Theory describes it as a (set of values + some operator + identity element).
While Category Theory describes how it composes with other structures. Thus, (idea of) monad is related (loosely) to the (idea of a) function, defined over a set of elements, while identity element is required for mathematical consistency, which can be composed with other functions in a certain way.
Why bother giving such a fancy explanation for something which looks awefully like an ordinary function then?
Such interpretation of (the idea of) a function comes with a bonus from mathematics, as an algebra, allowing various code gurantees as well as ‘mathematically correct’ composability with other abstractions, which come with their own algebras and guarantees as well as ‘mathematematically correct’… ad infinitum, while lambda calculus + syntactic sugar exposes it in haskell in the form of a Monad. For further reference: Set Theory and Category Theory.
It’s really a very basic idea, there’s not much to it, but it’s a fleeing simplicity:
It’s a value + sideffect.
It’s a way to sequence things together.
It’s a powerful building-block to write programs.
It’s the biggest haskell PR fail.
It’s just a monoid in the category of endofunctors.
It’s a burrito.
Here’s what it means in practice: The infamous >>=
helps us pass non-monadic values to functions without leaving a monad. In case of the Maybe monad, the monadic aspect is the qualifier that we don’t know with certainty whether the value will be… IO monad and do-notations gives as an elegant way to compose functions which arguably makes haskell the best language to write imperative programs.
In other words we sequence functional transofrmations of values in an intuitive way. It allows us to write complex functions in such a way, that instead of a horrible nested mess, we get a simple set of imperative statements. We are syntactically chaining functions together.
regularBurrito :: ()
= () regularBurrito
monadicBurrito :: IO ()
=
monadicBurrito do
...stuff...
return ()
and then, instead of
= regularBurrito stomack
you can write
<- monadicBurrito stomack
which is much more appropriate, is not it? It becomes much more obvious if we need to compose multiple functions over a value.
More examples of using Maybe monad with with and without sugar
-- | let x = foo in x + 3 corresponds to (\x -> x + 3) foo
-- | x <- foo; return (x + 3) to foo >>= (\x -> return (x + 3))
displayResult :: Maybe Int -> String
= maybe "There was no result" (("The result was " ++) . show) mx
displayResult mx
-- | fromMaybe 0 (Just 1) - interesting example of Maybe and extracting
-- | values from Just
add :: Maybe Int -> Maybe Int -> Maybe Int
=
add mx my >>= (\x -> my >>= (\y -> return (x + y)))
mx
-- | is equivalent to the following, using syntactic sugar:
add' :: Maybe Int -> Maybe Int -> Maybe Int
= do
add' mx my <- mx
x <- my
y return (x + y)
= undefined main
(creating bespoke monadic types)
Standard programming interface allows us defining ‘our own monads’ (monadic types):
class Monad m where
(>>=) :: m a -> ( a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
fail :: String -> m a
Basically our custom type needs to implement this interface. In addition, it needs to satisfy the following laws, in order to be consistent with monadic algebra:
return a >>= k = k a
>>= return = m
m >>= (\x -> k x >>= h) = (m >>= k) >>= h m
Once we’ve done that, i.e. once we’ve guranteed that our new type is monadic, we can use all the monadic goodies, e.g. >>=
, <-
, Monad Transformers, etc.