Title is literally true. This may not be the best place to learn about these concepts for the first time, because I'm going to focus on knocking down the misconceptions about them.
Then again, it may not be the worst place, for the same reason.
I had promised myself I would not add to the pile of functor or monad "tutorials", but I've been worn down. I gave up when I saw a reddit comment complaining about how Functor was "too hard to understand", which made me sad, because the correct response to the Functor interface is, "That's it?". And while Monad is legitimately a bit more interesting and complex, the correct response to that is not that different.
I am aware of the notorious effect that people "get" monads and then post their own idiosyncratic takes on them. In my defense, this isn't something I write just after my "ah ha!" moment, I've understood them in Haskell's context for many years now, and actually... this isn't even about that "ah ha!" moment at all. This is only about what they are. Even if you completely understand everything I write in this post, the real "ah ha!" where you realize just how useful the libraries built up around the monad interface are, the first time you search for a type on Hoogle where you're like this should exist and it turns out it does in fact exist already, that's still in your future. In fact I'm quite deliberately not trying to convey that feeling in the interests of getting at simply what the monad interface is. Which isn't, strictly speaking, a pre-requisite to that experience, but it does help.
After years of overly-acrimonious online discussion, Go is continuing down the chute towards getting generics. I'm already seeing the inevitable "functional" libraries (plural) coming out. I'm going to explain why none of these libraries are going to materially impact what the community considers good style.
They may see some use. I may even use some of them myself on small scales. Some iconoclasts will program with them extensively, and their code will work, in the sense that it will do what it was designed to do regardless of how the code reads. But they are not going to change what generally-good style is considered to be.
Edit: May 2021 - Update for the latest generics syntax.
There are a number of errors made in putative Monad tutorials in languages other than Haskell. Any implementation of monadic computations should be able to implement the equivalent of the following in Haskell:
minimal :: Bool -> [(Int, String)] minimal b = do x <- if b then [1, 2] else [3, 4] if x `mod` 2 == 0 then do y <- ["a", "b"] return (x, y) else do y <- ["y", "z"] return (x, y)
This should yield the local equivalent of:
Prelude> minimal True [(1,"y"),(1,"z"),(2,"a"),(2,"b")] Prelude> minimal False [(3,"y"),(3,"z"),(4,"a"),(4,"b")]
At the risk of being offensive, you, ahhh... really ought to understand why that's the result too, without too much effort... or you really shouldn't be writing a Monad tutorial. Ahem.
- Many putative monadic computation solutions only work with a "container" that contains zero or one elements, and therefore do not work on lists. >>= is allowed to call its second argument (a -> m b) an arbitrary number of times. It may be once, it may be dozens, it may be none. If you can't do that, you don't have a monadic computation.
- A monadic computation has the ability to examine the intermediate results of the computation, and make decisions, as shown by the if statement. If you can't do that, you don't have a monadic computation.
- In statically-typed languages, the type of the inner value is not determined by the incoming argument. It's a -> m b, not a -> m a, which is quite different. Note how x and y are of different types.
- The monadic computation builds up a namespace as it goes along; note we determine x, then somewhat later use it in the return, regardless of which branch we go down, and in both cases, we do not use it right away. Many putative implementations end up with a pipeline, where each stage can use the previous stage's values, but can not refer back to values before that.
- Monads are not "about effects". The monadic computation I show above is in fact perfectly pure, in every sense of the term. And yes, in practice monad notation is used this way in real Haskell all the time, it isn't just an incidental side-effect.
If you can translate the above code correctly, and obtain the correct result, I don't guarantee that you have a proper monadic computation, but if you've got a bind or a join function with the right type signatures, and you can do the above, you're probably at least on the right track. This is the approximately minimal example that a putative implementation of a monadic computation ought to be able to do.
A couple of months back, I analyzed whether I wanted to propose switching to Go for work. I've still technically got the blog post with the results of that analysis in the pipeline (though who knows when I'll get it up), but there's a part of it that keeps coming up online, and I want to get this bit out faster. It's about whether Go has "sum types".
Learn You a Haskell for Great Good! (A Beginner's Guide) by Miran Lipovača, published by No Starch Press (2011). No Starch was kind enough to send me an advance copy for review.
Haskell books for "real programmers" are still thin on the ground, being limited at the moment to Real World Haskell (2008) and possibly Programming in Haskell (2007). As its introduction states, this book is aimed at existing programmers who are currently fluent in something like Java, C++, or Python, and would like to learn Haskell.
I put my take on the traditional discussion of why you should consider learning Haskell in another blog post, so we can get on with the review here.
The hardest thing about learning Haskell with no previous functional experience is bootstrapping the strong foundation that you've long since taken for granted in your imperative language. If you don't have this strong grasp of the fundamentals, then every line of code is an invitation to get stuck on some subtle issue, and you'll never have the fluency that great work requires until you have that foundation.
This book is the best way I know to obtain the Haskell foundation you need for fluency.
No Starch Press asked me to write a review of the new Haskell book, Learn You a Haskell for Great Good!. I started to write a section about myself and my view of Haskell for context, and realized that it really needed to be its own post as it grew to a length where it was self-indulgent to make it part of the review. But it fits as its own post nicely.