A set of challenges for jump starting your understanding of monads.
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Now that you’ve spent some time using the monad abstraction, you may have seen that while it does what we need, it’s a little more verbose than we would like. This is where Haskell’s do notation comes to the rescue. When you write the following do block:
rule1 = do
foo <- calcFoo
bar foo
GHC automatically desugars this to
rule1 = bind calcFoo (\foo -> bar foo)
It’s important to note what this says about the type signatures of everything involved.
rule1 :: m b
calcFoo :: m a
bar :: a -> m b
The key is that whatever m
is, it must be the same for all three of these types. Also, calcFoo
returns an m a
, but bar
takes a plain a
. The bind function is responsible for “unboxing” the m a
and passing the unboxed value to bar
.
One thing that trips a lot of people up is what to do when they don’t have a function that looks like bar
. The version of bar
they want might instead have this type bar :: a -> b
(note that here we’re specifically saying that bar
’s type signature does NOT have an m
). Figure out what to do in this situation.
Note this challenge isn’t concrete. We’re dealing with more abstract types that don’t have type signatures exactly the same as the ones you’ve been working with. We could have written this challenge in a concrete way, but we think it’s important to learn how to think in more abstract terms. So for this go back to the stuff you have done before and find something that you think can be written with the do block that rule1
has. Or better yet, make up a new function that does it using Maybe
or Gen
.