
Inventing Monads - stopachka
https://stopa.io/post/247
======
ridiculous_fish
> This begins to get us to the fundamental abstraction of a monad: a box, with
> an interface for map, and flatMap

Before I understood monads, I read variations of the above sentence a million
times, always got stuck here:
[https://i.imgur.com/McThkuh.png](https://i.imgur.com/McThkuh.png)

How does this abstraction let us perform IO, do in-place destructive updates,
etc? The answer is that it _doesn 't_. These must be primitives supplied by
the runtime. A monad like IO has its two functions (return+bind), _and a bunch
of other magic functions_. It's obvious now but that fact is hardly ever
stated.

I wonder if anyone has tried explaining from the other direction? Rather than
building up to Maybe, try building down from a desire to print Hello World.

~~~
anchpop
What you say is correct, but let me try and explain a little further. The
Haskell function `getChar` is used to get a character from stdin. It has the
type `IO Char` and is a totally, 100% pure. In fact, it isn't even a function,
it's just a value.

You can write `getChar` as many times as you want, and you will get the same
result every time, and it will have nothing to do with whatever character is
being sent to stdin. Instead, what you will get is an instruction, saying
"please get a character from stdin".

What the IO monad allows you to do is to compose instructions together. For
example, you could write `getChar >> getChar". This makes a new instruction
that says "please get a character from stdin, discard it, then get a character
from stdin". The `>>` operator means "follow the instruction on the left,
discard the result, then do the instruction on the right".

Your _entire_ program is made by composing instructions like this, into one
new huge instruction that specifies your entire program's behaviour. You
assign that to the special name `main`. At runtime, the instruction is
executed.

The only bit of compiler magic that's necessary is the code for interpreting
the instructions and actually executing them. In theory, although haskell
doesn't let you do this, you could write `getChar` yourself, and just return
the same thing that `getChar` does, and it would behave identically.

This I feel is the simplest explanation for why monads are necessary in
Haskell. They allow you to conveniently specify what you want your program to
do, using only pure functions and values. Why do you want all values to be
pure? Many don't because it's kind of a pain, but Haskell kind of exists to
see if a lazy, pure functional language can be fun to use and many find that
it is. Hope this helps someone!

~~~
ridiculous_fish
Great description of IO, but I think it doesn't actually motivate monads. Your
example of `getChar >> getChar` could be simply `[getChar, getChar]`. Why
can't we just compose instructions into an ordinary list?

The answer is that we can, and Haskell used to work this way [1]! So as
'chowells' observed the monad-part of IO is almost incidental.

1: [https://stackoverflow.com/questions/17002119/haskell-pre-
mon...](https://stackoverflow.com/questions/17002119/haskell-pre-monadic-i-o)

~~~
a1369209993
> Your example of `getChar >> getChar` could be simply `[getChar, getChar]`.
> Why can't we just compose instructions into an ordinary list?

Because you can't pass results from a earlier list element into the
computation of a later element. You'd need a list with existentially-
quantified element types such as:

    
    
      {-# LANGUAGE ExistentialQuantification, GADTs #-}
      foreign import ccall "exit" exit :: Int -> IO ()
      infixr 1 :>
    
      data IOList a = forall b. (:>) (a -> Act b) (IOList b)
      data Act b where
        Getchar :: Act Char
        Print :: String -> Act ()
        Exit :: Int -> Act ()
      
      main' :: IOList ()
      main' = (\_->Getchar)
           :> (\c->Print ("Got '"++[c]++"'\n"))
           :> (\_ -> Exit 0)
           :> undefined
      
      runio :: a -> IOList a -> IO ()
      runio a (f:>fs) = flip runio fs =<< case f a of
        Getchar -> getChar
        Print s -> putStr s
        Exit n -> exit n
      
      main = runio () main'
    

where f :> g :> h :> ... is your list of instructions (rather than f : g : h :
..., which doesn't allow the response type of f to be related to the argument
type of g).

------
cjfd
In various places on the web one can find the quote that 'Dependency injection
is a 25-dollar term for a 5-cent concept'.

I feel it is the same with monads including all the false suggestions that one
might need to understand category theory and similar such nonsense.

~~~
edflsafoiewq
If only DI had a clear definition like "monad" does, I might be able to
understand what it is.

~~~
goto11
It just means passing a dependency as an argument to method or constructor.
Seriously, it is just that!

~~~
Twisol
Which, in FP, is all but invisible: the arguments to a function literally are
its dependencies.

I think OOP has more primitive concepts (and more mutation) than FP, so
dependency injection in OOP also includes object construction and often
mocking effectful operations. That's why it gets its own name in OOP, while
being more of an ambient idea in FP.

~~~
goto11
I don't think I agree with that. In scheme you can write (display "hello
world") inside a function and this is directed to some globally configured
port. If either the port or the display function was passed as a parameter to
the function, then it would be dependency injection.

~~~
Twisol
On its face, you're right. You can formalize this approach using dynamically-
scoped variables, which is a step on the road toward coeffect systems in typed
functional languages. But in a coeffect system, the type of `display` would
explicitly call out that its environment must provide the necessary dynamic
variables, which brings us back to dependency injection. `display`'s
dependencies would be injected _via_ dynamic scope, which you can override in
the caller by defining a dynamic binding.

As an alternative, I would suggest that `display` is special, and that instead
of thinking of passing an extra parameter to `display`, that the module that
calls it should instead have `display` _itself_ injected.

~~~
goto11
Yes that is actually what I meant, I guess I was not being clear. The function
calling "display" could have "display" passed as an argument rather than
calling it as a globally defined function.

Relying on dynamic variables would probably not be considered dependency
injection. A major purpose of DI is that dependencies should be declared in
the signature, making it explicit which dependencies a unction or object
depends on.

~~~
Twisol
> A major purpose of DI is that dependencies should be declared in the
> signature

Yes, and a coeffect system would cause dependencies on dynamic variables to be
declared statically, even if they're provided by "the environment" at runtime.

Tomas Petricek's PhD project page is a good introduction to coeffect systems,
and it illustrates dynamic variables as an example.
[http://tomasp.net/coeffects/](http://tomasp.net/coeffects/)

------
chowells
There's also the classic [http://blog.sigfpe.com/2006/08/you-could-have-
invented-monad...](http://blog.sigfpe.com/2006/08/you-could-have-invented-
monads-and.html)

Written by a 3-time Oscar winner, no less!

~~~
guerrilla
> Writen by 3-time Oscar winner, no less!

Sorry, what? Their About page is blank.

~~~
dllthomas
The author is Dan Piponi: [https://pwlconf.org/2018/dan-
piponi/](https://pwlconf.org/2018/dan-piponi/)

[https://www.imdb.com/name/nm0685004/](https://www.imdb.com/name/nm0685004/)

~~~
scns
2015 - Technical Achievement Award, shared with Kim Libreri, George Borshukov:
For their pioneering work in the development of Universal Capture at ESC
Entertainment.

2014 - Technical Achievement Award, shared with Olivier Maury, Ian Sachs: For
the creation of the ILM Plume system that simulates and renders fire, smoke
and explosions for motion picture visual effects.

2001 - Technical Achievement Award, shared with George Borshukov, Kim Libreri:
For the development of a system for image-based rendering allowing
choreographed camera movements through computer graphic reconstructed sets.

------
ducaale
To understand monads, I think it helps to know a language where expressing
such a concept is more natural. Although I do not claim to know 100% what
monads are yet, coming across these features made it easier for me to
understand the concept a little bit.

\- Algebraic data types

\- OCaml's let expressions

\- F#'s computation expressions

I also think that it is important to write something that naturally requires
the use of monads. One thing that spring to mind is Parser Combinators.

\- [https://fsharpforfunandprofit.com/posts/understanding-
parser...](https://fsharpforfunandprofit.com/posts/understanding-parser-
combinators/)

\-
[https://www.youtube.com/watch?v=N9RUqGYuGfw](https://www.youtube.com/watch?v=N9RUqGYuGfw)

------
raiflip
I really like how this article builds up from a simple real life use case.

Shameless plug for my own article that did something similar about a week ago:
[https://medium.com/@ameltzer91/an-easy-to-understand-
monad-g...](https://medium.com/@ameltzer91/an-easy-to-understand-monad-guide-
fdad10356a9c)

~~~
jjjbokma
Nice article, I linked to it from my tumblelog
[https://plurrrr.com/archive/2020/08/19.html](https://plurrrr.com/archive/2020/08/19.html)
10 days ago. Thanks for writing this.

------
centimeter
I think the hard part for most people is moving beyond Maybe/Either. It’s hard
to find motivating examples when most languages lack denotational semantics
and everything implicitly happens inside IO all the time.

~~~
Scandiravian
That is something I've struggled with as well. Do you have any suggestions for
resources, that could help overcome that challenge?

~~~
ryanjshaw
If you come from a C# background, this book is excellent:
[https://www.manning.com/books/functional-programming-in-c-
sh...](https://www.manning.com/books/functional-programming-in-c-sharp)

It does have some minor shortcomings in my opinion (e.g. if you haven't
already started on the path of reinventing monads yourself you may struggle to
immediately understand why this is a big deal and how it will save your life,
the author should have used LanguageExt instead of their own library as
LanguageExt is actively maintained and extremely well thought out, the book
stops just short of becoming practical in the sense of "here's how to start a
new C# project while thinking in functions", etc.).

You might also consider the LanguageExt guide itself:
[https://github.com/louthy/language-ext/wiki/Thinking-
Functio...](https://github.com/louthy/language-ext/wiki/Thinking-Functionally)

~~~
Scandiravian
I actually found the LangExt package a few months ago, when I started a new
job and had to use C# as the default language (I've primarily been using
Python, Typescript, and Rust in my previous jobs)

I spent some time explaining the advantages of an FP approach to my colleagues
and LangExt has started to pop up in their PRs, which I'm very happy about

It's definitely a great library and I'm really impressed with the effort
that's being put into it!

I'll give the book you're suggesting a read. I think it might help cover some
of the gaps in my knowledge, which is exactly what I'm looking for, so thank
you for the suggestion

------
touchngthevodka
As someone who isn't familiar with functional programming, what benefit does
this give us over throwing an error when trying to access a resource that
doesn't exist?

~~~
ianhorn
A monad's special function application lets you write much simpler code in
certain situations.

Say you're working with some data structure that contains/emits numbers: a
pointer to a resource containing a number. A list of numbers. A function that
returns a number. An optional number (or null).

A common operation is unpacking that structure to get a number, applying a
function to the number, and packing it back up: Reading from the pointer,
applying the function, and returning a pointer of the result. Applying the
function to each element of the list and returning a list of the results.
Composing a function with another function. Applying a function to the
optional number or just returning the null.

When you're writing code on this, it's error prone to do the unpacking,
application, and repacking. It's much simpler if you can write code that looks
like `def f(x): return exp(x)/x + 23`. Much more testable too. If you have two
or more of these structured things, it might get even more error prone. It's
much easier to write code that takes three integers and does stuff, instead of
writing code that takes three pointers/lists/functions/optionals.

Monads are part of a hierarchy that abstracts that. Anything that defines that
sort of function application in a particularly convenient way is a monad.
There's more to it, but that's why it's useful.

It lets you write code dealing with the things in your data structure, letting
you mostly ignore the structure itself.

\-------

In this specific situation, say you want to replace your error handling with
something else. Maybe it writes to a log file then errors. Or maybe it does
something fancier. Or maybe you even change the way you get the resource as
well as the erroring to something fancy. As you swap out the "structure" code,
with a monad it's just switching to a new monad, rather than refactoring the
business logic related code. It's a nice separation of concerns.

~~~
Izkata
> Anything that defines that sort of function application in a particularly
> convenient way is a monad. There's more to it, but that's why it's useful.

Aaand with this statement you skipped over what IMO is the most important
missing piece, because everything above it fits higher-order functions such as
map(), which as far as I understand aren't monads.

~~~
ianhorn
I disagree. Map (or a functor) alone can't do everything I've said. There are
a few reasons. I assume you're already familiar with the subject matter, so
I'm going to cross my fingers that you know it in haskell and use that
notation and terminology to save us time.

tl;dr map works for applying the simplest functions to "containers." To apply
more interesting functions, you need bind, pure/return, and/or whatever
applicative's <*> is called.

Let's say you're working in something like Maybe. You might want to write some
code like

    
    
        f :: Float -> Float
        f x = 5 + x
    

In that case map is fine. fmap lets you focus on simple code like that. Or
maybe you want to write

    
    
        f :: Float -> Maybe Float
        f x = if x == 0
              then None
              else 5 / x
    

In that case you need more than map, because you don't want to deal with what
map would give you: a Maybe (Maybe Float). (>>=) lets you still focus on
simple code like this, since you dont have to deal with any
unpacking/flattening, which was what I'm saying is why monads are so useful.

More importantly, you need more of the FAM hierarchy than map if you care
about multivariate functions, which I'd say is most code. Lets say we're
working with

    
    
        f :: Float -> Float -> Float
        f x y = x + y
    

We want to write code like that and use some version of function application
(like map). If we just use map, we get the following

    
    
        f <$> maybeX :: Maybe (Float -> Float)
    

which isn't at all what we want, because we can't apply it to a maybeY (or
even to a float y, which `pure`/`return` lets us treat as a maybeY). If we
define an additional way to apply that Maybe (Float -> Float) to a Maybe
Float, we've defined Applicative, forcing us to go beyond a functor.

The motivation and usefulness is the same throughout: we just want to write
code and apply functions that don't care about the structures
emitting/containing our inputs and outputs. It just turns out that there are
three cases depending on the kinds of functions we're writing and applying

    
    
       f :: a -> b  -- functor is sufficient, like you say.
       f :: a -> b -> c -- functor isn't sufficient. applicative is.
       f :: a -> m b -- functor and applicative aren't sufficient. monad is.
    

I wrote a series of posts ages ago deriving all these from that one motivation
(in a more fleshed out manner) [http://imh.github.io/2016/05/26/why-
monads.html](http://imh.github.io/2016/05/26/why-monads.html)

~~~
Izkata
> I assume you're already familiar with the subject matter

Nope. I think this is why you don't see where the previous post falls short,
too much familiarity so you don't realize you're skipping over important
aspects.

> so I'm going to cross my fingers that you know it in haskell and use that
> notation and terminology to save us time.

I get just enough Haskell to understand the first 3 code blocks, and can guess
what the 4th is depicting, but am not sure.

~~~
ianhorn
Sorry, I thought when you said missing piece in your previous message, that
you meant the missing piece of what makes monads unique (arguing from a place
of knowledge about “no THIS is what makes monads important”), rather than the
missing piece of the explanation. Either way, hopefully the posts I wrote and
linked are written assuming nothing more than the basic Haskell syntax, so if
you’re curious about the rest of the explanation, hopefully it’s clearer
there. If it’s not clear there, I’d appreciate any feedback.

------
ape4
That code that "is getting pretty ugly" seemed ok to me.

~~~
sfvisser
Sure, that example wasn’t that bad. But if your specific monad is somewhat
more complicated - e.g state, either, promise - and you’re using it all over
the place all that boilerplate is going to obscure your domain logic pretty
quickly.

Monads can help readability by focusing on the composition of your domain
logic instead of writing out the same low-level boilerplate over and over
again.

------
codygman
I had a similar example today in the functional programming slack.

> hello, quick question: i feel like there's a more elegant way to express
> this, but I'm struggling to come up with one:
    
    
        foo :: (a -> Bool) -> (a -> Bool) -> a -> Bool
        foo f g x = f x && g x
    

For those who aren't as familiar with Haskell, that is taking a function f and
a function g, passing argument x to both, and then doing a binary and on their
boolean result.

One answer suggested:

> You can get fancy, but the “simple” version is almost always more readable:
    
    
        (not . null . f $ x) && (not . null . g $ x)
    

Which I find myself agreeing with in many ways, but for some reason leaves me
desiring more.

The answer to that desire complements this article I think:

> if f and g returned Maybe you could have:
    
    
        > f _ = Nothing
        > g _ = Just ()
        > x = ()
        >  (f x) <*> (g x)
        Nothing
        > import Data.Maybe
        >  (f x) <*> (g x) & isJust
        False
        > -- or to avoid <*> you can do
        > liftA f g x
        Nothing
    

If that's not clear, let me know what's confusing and I'll try to explain
further

~~~
sudomakeup
So, I'm a bit rusty on haskell but I have some notes on a similar concept.
Essentially, fmap with a twist - instead of applying the same function to a
list of values, you have a list of functions that you want to evaluate on the
same value. "fpam". In this case, we're dealing with a list of size 2

    
    
      fpam :: [(a -> b)] -> a -> [b]
      fpam fns v = fns <*> pure v
    

after that then fold the list with boolean &&

    
    
      foo :: (a -> Bool) -> (a -> Bool) -> a -> Bool
      foo f g x = foldr1 (&&) ( fpam [f,g] x)
    

or alternatively with no helper functions

    
    
      foo2 :: (a -> Bool) -> (a -> Bool) -> a -> Bool
      foo2 f g x = foldr1 (&&) ( [f,g] <*> pure x)
    

for example

    
    
      Prelude> foldr1  (&&) ( [ (==3), (==4) ]  <*> pure 3 )
      False
    

Alternate implementations:

    
    
        import Data.List
        import Data.Function
        import Control.Monad.State
        import Data.Foldable
        import Control.Arrow((>>>))
        import Control.Monad.Reader
        import Control.Monad.List
    
        applyList :: [(a -> a)] -> a -> a
        applyList list = execState $ for_ list modify
    
        applyList2 :: [(a -> a)] -> a -> a
        applyList2 = foldr1 (>>>) 
    
        fpam :: [(a -> b)] -> a -> [b]
        fpam fns v = fns <*> pure v
    
        fpam2 :: [(a -> b)] -> a -> [b]
        fpam2 fns = runReader $ forM fns reader
    
        fpam3 :: [(a -> b)] -> a -> [b]
        fpam3 fns v = fmap (\f -> f v) fns
    
        fpam4 :: [(a -> b)] -> a -> [b]
        fpam4 fns = runReaderT $ do
          fn <- lift fns
          reader fn

~~~
codygman
Also see this related discussion:
[https://www.reddit.com/r/programming/comments/ij0qzl/comment...](https://www.reddit.com/r/programming/comments/ij0qzl/comment/g3dzohz)

------
kjgkjhfkjf
How many Monad explanations need to be written before we admit that the Monad
is an unsatisfactory abstraction for practical purposes?

~~~
mrkeen
These things are monadic:

Functions, continuations, state, IO, parsers, futures, streams, lists,
transactions, LINQ, observables.

I can only think of one abstraction more useful - the function.

~~~
kjgkjhfkjf
I didn't say that monads aren't useful; I said that the abstraction is
unsatisfactory. The world needs a new clearer concept to use instead of the
monad, just as raft can be used instead of paxos.

~~~
chombier
Monads are literally the simplest/smallest abstraction that makes all these
examples compose nicely. It will be hard to come up with something clearer I
think.

What is so unsatisfactory about them?

------
kbr
I wrote an article with the same name! [1] The problem solving based approach
seems to be the most relatable and easy for people to get an idea of what
monads are. Nice work.

[1] [https://blog.kabir.sh/inventing-monads](https://blog.kabir.sh/inventing-
monads)

~~~
stopachka
Great work Kabir! : )

------
k__
I think, the main benefit of FP is alsoits main problem.

Patterns like the monad are so abstract, that ob its own nobody knows what to
do with it, but it makes them so very powerful.

OOP patterns, on the other hand, come from a more inductive source. They are
more concrete, but also not as powerful. Easier to grasp, but less concise.

We need a step (or more) between the definition of FP concepts and their
application, to make all this more approachable for the average programmer.

------
whereistimbo
I thought I understand monad after reading the article, but then I read the
comment and now I am confused what is monad (again)?

~~~
willtim
I recommend reading the original "Monads for Functional Programming" paper by
Phil Wadler, it's in a tutorial style and IMHO still better than any blog post
explanation I have seen:

[https://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/b...](https://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf)

------
renox
I wonder why it isn't ever described as a design pattern? For me, it is one..

~~~
mrkeen
I would call the State Monad a design pattern, I wouldn't call the List Monad
a design pattern.

------
tarkin2
[Edited away]

------
fizixer
So do you need monads in Haskell because that's the only way you could do some
of the things that are trivial in an imperative language (albeit verbose and
arguably inelegant)?

~~~
Koshkin
Yes. (For example, ML, being impure, does not need this.)

~~~
dependenttypes
Haskell only "needs" monads for IO (there are other alternatives to implement
io in a pure language too though). The reason that they are popular in haskell
is that they are convenient and the reason that they are not popular in ML is
that it lacks higher order types and things like typeclasses.

