The thing that's been bothering me with Haskell more than anything else is certainly type checking. Although, that's my favourite feature from the language. Problem is, when you're just a beginner, it's just much too hard to understand things and move forward, if you're more like me, you start poking around until you got it to compile, shut lid of your laptop and fall asleep because it's way after midnight.
Since this is not a (yet another) Monad Tutorial, to learn what Monad, Applicative and Functor are, it is better to refer to one of good sources to read about Monads themselves. The following two links have been most helpful for me personally:
For more information and links on how to start with Haskell, I suggest referring to Chris Allen's Learn Haskell repo.
Several things to keep in mind while reading this article:
Integer -> Integeris read like "receives
<-is read like "is coming from"
Monadis also an
Applicative, which is in turn a
Functor. Keeping that in mind may help you to find easier/better fitting constructs.
IOin all the examples. This was done intentionally, since everyone constantly says that
IOis difficult in Haskell, and
Maybeis so awesome. But composing things wrapped into
IOcontext is just as easy and convenient as composing things wrapped into
Maybecontext, so you may replace
Maybe(or any other "context") in most of examples and yield same exact results.
undefinedplaceholder for them. This was done for you to be able to copy-and-paste the code straight to your favourite editor and try playing with them / seeing that they type check without providing implementations or even thinking about implementation details.
$is just a function application:
f $ x = f x, helps to save some brackets.
After going through first several Monad Tutorials™, it was clear that monad is just a Typeclass, and there are many things that are, in essence, monads. Although when it came to implementation, there was no good intuition, so I had to guess some things, or try infer them from type signatures, which was not so easy.
I've taken a notebook and starting writing things down for myself: when there's
X on left hand side, and
Z on right hand side,
Y should come in the middle.
This way I was able to start from either
Z and just fill the blanks.
After a couple of repetitions things started getting more and more automatic, and
eventually I've mostly stopped noticing presence of monads in many parts of the
We all learn differently, and for me it got much easier when I started seeing these patterns. You may say it's easy to infer all the required information from type signatures, and would be true, but if you already understand signatures perfectly well, this post is not for you.
The main purpose of this article is for you to gain some intuition on when to use, what, and imagining which "visual" pattern that signature may be mapped to.
return is the simplest combination available in
monadic stack, and everyone seems to get it quite fast.
intOp :: IO Integer intOp = undefined main = do -- ↓ here `i` is of type `Integer` i <- intOp -- in order to get it wrapped back to `IO`, we need to use `return` return (i + 1)
Even though I'm using
IO monad here, same exact thing is
possible with any other monad.
In the same way, you can combine things that are coming from several contexts:
intOp1 :: IO Integer intOp1 = undefined intOp2 :: IO Integer intOp2 = undefined main = do i1 <- intOp1 i2 <- intOp2 return (i1 + i2)
Now, let's imagine, we have one function that returns
and second one that returns
Maybe Integer, and we'd like to return
a sum of them. Most likely the type of the desired result would be
IO (Maybe Integer):
intOp1 :: IO Integer intOp1 = undefined intOp2 :: Maybe Integer intOp2 = undefined main2 :: IO (Maybe Integer) main2 = do i1 <- intOp1 return $ (i1+) <$> intOp2
Here, several things are worth mentioning:
Integer -> Integer
Maybe Integer, and
Functor, we can use
<$>to apply an
Integer -> Integeroperation to
<$>is actually same exact thing as
So somewhere in my imagination the last line looks like:
(Integer -> Integer) <$> (Maybe Integer)
And it would return
Maybe Integer. In order to add our desired
shuold only add
return before it.
liftM was especially useful for me when writing some convenience functions
that operate on things within a monadic context. For example, if you have
IO (Integer, String) (tuple within an
IO context) and you'd like to
extract the second part of tuple (which is
String), you can use
liftedSnd :: IO (Integer, String) -> IO String liftedSnd = liftM snd
Here, we've converted a function with signature of
(a,b) -> b to the function
that operates within
IO (a,b) -> IO b.
Once again, there's nothing specific about IO here, so you may as well use it with any other Monad.
You may have also noticed that
liftM here may be as well replaced by
extractSndFromTuple :: IO (Integer, String) -> IO String extractSndFromTuple tuple = snd <$> tuple -- Or even extractSndFromTuple = fmap snd
liftM however has version with 2 arguments, called
liftM2, which is very
useful when you'd like, for example, sum several things, wrapped into
For example, if you want to concatenate two
Strings wrapped into
IO, you can
concatenateIOStrings :: IO String -> IO String -> IO String concatenateIOStrings = liftM2 (++)
You may as well use it with
concatenateMaybeStrings :: Maybe String -> Maybe String -> Maybe String concatenateMaybeStrings = liftM2 (++)
Or even say that your helper is valid for every Monad:
concatenateMStrings :: Monad m => m String -> m String -> m String concatenateMStrings = liftM2 (++)
To put it simply,
liftM* functions are used to apply a "pure" function to
the values wrapped within a monadic context.
Just for the reference, there are also versions of
liftM for 3, 4, 5
Monad is an
Applicative and a
Functor, you can use
<*> to yield the same exact result:
concatenateIOStrings :: IO String -> IO String -> IO String concatenateIOStrings ioStr1 ioStr2 = (++) <$> ioStr1 <*> ioStr2
To generalize it all a bit, and get rid of tying yourself to
can say that this function is also valid for everything that is an
concatenateAStrings :: Applicative a => a String -> a String -> a String concatenateAStrings ioStr1 ioStr2 = (++) <$> ioStr1 <*> ioStr2
To be honest, reading type signatures for
<*> didn't help
me at first. Although seeing this pattern repeated by several people
in different projects have helped me to understand it all better.
Generally, using Applicative instances is better than using Monad ones,
and combination of
<*> is more idiomatic and preferred in
Haskell. Almost every monadic operation has it's Applicative counterpart,
and we'll talk about it in the next parts.
Thanks for reading that far. I'm planning to write a second part of the
post, where I wanted to pretty much go on in the same spirit and
join, a word or two about
transformers, and show several combinations that I've learned "hard way".
If you liked this post, and would like to see the second part, please ping me on Twitter. It'd be good to know whether there is any demand. If you're a seasoned Haskell developer, and know how this post could be improved, please get in touch, too!
If you like my content, you might want to follow me on Twitter to subscribe for the future updates!