Notes on working monadically
20 Jun 2014You can do little bits and pieces in your Haskell code to take it from an imperative looking style to being more concise and monadic in your approach. In today’s post, I’m going to run through a small sample of functions that should help this process greatly.
One final thought is that it’s not so much the use of these functions that’s important; it’s more the change in thought process to put you in a position where these functions become effective is where the true power lies.
»=
The >>=
function (pronounced “bind”) sequences operations together by passing the result of what’s on the left hand side, to the right hand side. >>=
looks like this:
>>=
is asking for:
a
which is a value wrapped in a monad(a -> m a)
which is a function accepting a value and returning a wrapped value
An example of this in action looks like this:
What’s going on in the above snippet is that >>=
has unwrapped the string returned by ns
from its IO
monad so that it’s just a string. >>=
has then applied this raw value to putStrLn
.
=«
The =<<
function performs the same role as >>=
only it has its parameters flipped.
liftM
liftM
allows you to promote a function to a monad. Here’s what it looks like
(a1 -> r)
is what will get lifted into the monad, so we can keep our code that does work free of any monad awareness.
Where I have found that this is useful is when you have a set of functions that don’t operate on wrapped values, and you’d like to sequence them monadically.
My initial attempts to do this look like this:
This reads like a big blob of imperative glug! So, I thought that all of these pieces could be sequenced together and chained with >>=
. Here’s what I got:
Well, at least the code is sequenced - but all of those return
s sure are annoying. Here’s where liftM
comes in. With liftM
, we can compose all of those functions without needing to know anything about the monad that it’s executing in. Here’s what I’ve ended up with:
liftM
has allowed us to express our function chain using .
as function composition. liftM
then handles all of the monadness for us!
»
The >>
function performs he same sequencing as what >>=
does, only the first action specified is discarded. >>
looks like this:
This particular function comes in handy where you’re interested in not passing along a result from certain links in your sequencing chain, like this:
From this particular sequence, you can see that
- The action emitted from the first
putStrLn
is dropped - The action emitted from
getLine
is passed ontoputStr
- The action emitted from
putStr
is dropped - The last action terminates the sequence
sequence
sequence
will evaluate all of the actions passed to it from left to right and return out the results of these actions. It’s defined like this
What this allows you to do is something like this
This will then give you back an array of the IO
actions emitted from each array index.
sequence_
will perform the same task as what sequence
does, only it’ll throw away the result.
mapM
mapM
will allow you to perform a monadic action over a list of normal (or unwrapped) values. It looks like this
mapM
wants:
- A function that takes an unwrapped value
a
as its first parameter and returns a wrapped valuem b
- A list of unwrapped values
[a]
It will then give you back a list of wrapped outputs m [b]
. So, this allows you to do something like this
Also notice that in the lambda above (\q -> putStr q >> getLine)
, we’ve used the >>
function from above as we don’t care for the action emitted from printing a string to the console.
mapM_
will perform the same task as what mapM
does, only it’ll throw the result away.
filterM
filterM
will filter a list based on a Bool
wrapped in an action. Here’s how filterM
is defined
filterM
will execute an action (a -> m Bool)
that wants an a
as its input and returns you a wrapped m Bool
which will determine which a
’s in the passed list [a]
end up in the resulting wrapped list m [a]
. Mouthful, I know. Here’s an example
The lambda here (\_ -> randomIO >>= return . even)
actually ignores the input parameter by using \_
. It’s using randomIO
to grab a number out of the hat which is then bound to (return . even)
, which will return a wrapped Bool
of if the number supplied by randomIO
is even or not.
There’s heaps more that you can do. Just check out the Control.Monad namespace of the base library for some more. That’s it for today though!