type : Monad
Abstraction
pure :: a -> m a
- Wrap a value in the monad type
m
. map :: (a -> b) -> m a -> m b
- Map a function across a monad value.
flatMap :: (a -> m b) -> m a -> m b
- Map a monad returning function across a monad value.
Example
The simplest monad instance is the one for Identity
.
Identity a
- A type that holds a single value of
a
. mkIdentity :: a -> Identity a
- Construct an
Identity
. getIdentity :: Identity a -> a
- Retrieve the wrapped value.
The monad instance:
pure :: a -> Identity a pure = mkIdentity map :: (a -> b) -> Identity a -> Identity b map f ia = identity (f (getIdentity ia)) flatMap :: (a -> Identity b) -> Identity a -> Identity b flatMap f ia = f (getIdentity ia)
Now imagine a computation that concatenates a list of strings then evaluates their length.
concat :: List String -> String length :: String -> Int ss = [ "ab", "cd", "ef" ] s = concat ss length s -- => 6
Using Identity
the computation would look like this:
map (\s -> length s) (map (\ss -> concat ss) (pure [ "ab", "cd", "ef" ])) -- => Identity 6
That looks ugly and gains us nothing. What if the function signatures were different?
concat :: List String -> Identity String length :: String -> Identity Int flatMap (s -> length s) (flatMap (\ss -> concat ss) (pure [ "ab", "cd", "ef" ])) -- => Identity 6
Still ugly. All we've done is switch map
for flatMap
. What if we alias
flatMap
to an operator, ;
?
x ; f = flatMap f x pure [ "ab", "cd", "ef" ] ; \ss -> concat ss ; \s -> length s -- => Identity 6
Add newlines and:
pure [ "ab", "cd", "ef" ]; \ss -> concat ss; \s -> length s -- => Identity 6
Compare to the initial non-identity code and you can see that Monad
gives us
what some have called, “programmable statements”.
-- Statements ss = [ "ab", "cd", "ef" ] s = concat ss length s -- Monadic statements pure [ "ab", "cd", "ef" ]; \ss -> concat ss; \s -> length s
Some languages provide syntax sugar for these operations.
-- Haskell and Purescript do ss <- pure [ "ab", "cd", "ef" ] s <- concat ss length s
// Scala for { ss <- pure(List("ab", "cd", "ef")) s <- concat(ss) l <- length(s) } yield(l)
// C# from ss in pure(new[] {"ab", "cd", "ef"}) from s in concat(ss) from l in length(s) select l
So what? All we've achieved so far is another way of writing statements.