47 comments

[ 3.2 ms ] story [ 85.1 ms ] thread
A monad, as thought of in programming, is really something that requires higher kinded types.

The reason they are hard to explain in a language without HKT is the same reason why List[T] would be hard to explain in a language without generics.

edit: The article is impressive. It's just it feels like a different thing to me to than what monads are in haskell. I guess it would have to be.

agreed but still impressed by authors effort
yep. as the OP said: There is a reason why most monad tutorials use Haskell: without syntactic support, monads are pretty clunky to use.

(You could probaby go further and say "without semantic support.")

You can have monads in clojure (eg: http://funcool.github.io/cats/latest/#monad) but the problem I see is that they are infectious and kinda force you to use them everywhere. In other languages, it's more palatable because the compiler checks for types, and normally there is some sugar to make it easier on the eyes. Besides, I think monads are a bit off the clojure way, even if they happen to exist somehow (eg promesa and manifold libraries).
And without the compiler checking the types, they're nightmarish to use. It falls to pure human sweat to track if a variable holds a `thing` or a `Monad Thing`. In my humble experience, it leads to lots of tedious banging around in the REPL to track down where you made a mistake. This is especially painful while refactoring.

I've given up on the monads in dynamic languages outside of really strict confined situations (validation for instance). I think it's ultimately a bit silly to do something like `StateT` in Clojure rather than just using an `atom` or whatever. What you lose in referential transparency and purity, you more than make up for in sanity over the long haul.

The scales would tip if Clojure had stronger typing options, but for now, using the idiomatic stuff is the way to go. Although, spec could probably be used in a pinch, but it's kind've hot garbage compared to compiler checked types (imho!).

(comment deleted)
It's not that monads "require" higher kinded types, it's that , like an interface, monads only start really "buying" their "cost" when there are _many_ different things that can be monads.

Then the common abstraction of the monad can be used to interact with these things that all share this common property, in a unified way.

Finally, higher kinded types is what makes the idea of _many_ different things that can be monads manageable for the programmer, because if the underlying thing that the monad represents keeps changing into different forms, it's very easy to loose track of what structure or concept it currently represents without the types to keep track of that for you.

yeah, that's better put.

The usefulness of it, to me, is how the bind/flatmap can be generic over type constructors, but type constructors aren't a clojure concept. It's a static typing thing.

To have an interface for monads, you need to have HKT don't you? To declare the type of bind?

It could be useful otherwise. It would just seem like a slightly different thing.

You can also theoretically go the completely opposite direction and use dynamic typing. You have the usual tradeoffs related to that, plus maybe a couple new wrinkles, but it can work.
Someone once told me “continuations are isomorphic to monads” and I eventually realized that the best way to do something monad-like in JS was to write functions that take a callback as their last argument and then higher order functions that manage threading the callbacks around, occasionally transforming it to slot in impure behaviors.

I really disagree with the notion “HKT are necessary for monads”, though: they’re necessary to statically check that your monadic code is correct, but asside from things that rely on return-type inference, you can just implement those type-checks as runtime checks (or leave them out entirely): if the architecture of side-effects in your app is arranged around monadic principles, you can write all the same generic transformations (and, in fact, designing your dynamically typed code this way can get you a good fraction of the correctness benefits of static typechecking)

One example here. CLOS-style multimethods are a reasonably good runtime analog of the way typeclasses work: typeclasses use type inference to thread a dictionary of functions around, multimethods do roughly the same thing with dynamic type-checks. This means multimethods have overhead typeclasses don’t have, but it also means that multimethods don’t have the “closed-world” assumption and can be extended dynamically.
I think there's a lot of people confusing the bricks of functional programming with what you can build with them.

The interesting thing about 'monads' isn't bind and return. The interesting thing is the Control.Monad functions that build on them, and by making something that conforms to the monadic interface, you get to use them: https://hackage.haskell.org/package/base-4.15.0.0/docs/Contr...

Just like the special thing about iterators isn't that you can get the "next" thing. That's not impressive on its own. The magic is in the tools you get that can generically use and manipulate iterators if they conform to a particular interface.

Exactly this. The power of an abstraction is roughly the product of two metrics:

1. How many things "fit" this abstraction?

2. What does this abstraction let me say?

The abstraction "Monad" has many inhabitants, and the Control.Monad module shows that it lets you say many things -> it is a very useful abstraction. The abstraction "Applicative" has many more inhabitants, but lets you say fewer things -> it is still a useful abstraction.

I think this is only partly true.

The other big benefit of monads is that they can automatically work with syntactic-sugar such as do-notation to allow non-linear code to be written in a linear way.

Monads and syntax, even without HKT, allow the user to write their own async-await, option, result, list, etc. expressions.

(comment deleted)
What is described looks to me like an interpreter for a Forth-like DSL in which the notion of the monad should find its "natural" expression. Still not sure if it can indeed be viewed as a practically useful extension of the base language (Clojure)...
I'm not an expert, but my understanding is:

1. You are writing a DSL that works with wrapped values.

2. In this DSL you can use arbitrary pure functions from your base language.

3. The value proposition is: those pure functions should not be aware of the fact that they are working with wrapped values, they are written as if they are computing regular non-wrapped values. Bind takes care of this.

4. Indeed the simplest way to implement this is some kind of interpreter. But you don't have to re-implement the entire language inside this interpreter, because of #3

I think Monad make more sense when you understand the context and its additional constraints as to why you'd want to use Monads.

Imagine that you want to write code that is strictly pure, so that when evaluated, it doesn't do any side effect, it only returns a pure description of the computation it did.

Here's a simple example:

    func announce-good-catch(fish)
      if(fish.size > 5)
        print "We found a big one!
      else
        print "That one won't even feed a cat, try again!"
How can you make this function pure? It technically prints something, yet you want to run it and not have it print, but instead return you something that describes what branch it took and what effect it would have done had it been impure.

You can do this:

    func announce-good-catch(fish)
      if(fish.size > 5)
        return [:print "We found a big one!]
      else
        return [:print "That one won't even feed a cat, try again!"]
Now the function is pure, it doesn't print anything, it evaluates the branch to take based on input and return to you a description of what it should do.

The problem is programming that way is hard, let's add another function:

    func get-fish(fish-id)
      return new Fish(db.query("select * from fish where id = " + fish-id)
Again this isn't pure, so let's make it pure:

    func get-fish(fish-id)
      return [:new Fish [:query [:get :db] ["select * from fish where id = " + fish-id]]])
Already this is getting pretty complicated, getting the DB is impure so return a description of it, querying the DB is impure, so return a description of it, Fish can't be created if we don't execute the side-effect, so take note that you want to create a Fish after those two side-effects were to actually happen.

Now combine get-fish and announce-good-catch(fish)?

    announce-good-catch((get-fish 10))
Well this works in the impure version, but this doesn't work for our pure versions because now the input to announce-good-catch isn't an actual Fish but instead it's this:

    [:new Fish
     [:query
      [:get :db]
      ["select * from fish where id = 10"]]]
And so it's not going to work, you can't compose those pure functions as you'd normally would.

Here is where Monads become really handy, they put all this behind an abstraction that lets you with added syntax sugar feel like your code is written in the impure style, yet under the hood it will convert things to a description of all side-effects and whatever pure behavior is pending the result of those. And it'll let you compose those descriptions together so that from the programmer point of view the code you write looks and feels almost the same as the impure version.

This is the problem Monad solves.

Now once you have that description, eventually you want it to actually run for real so that side-effects and all other described pending behavior gets executed. But what people realized here is you can kind of abuse this a bit, where you could choose to interpret the description in new ways, or manipulate the description prior to executing it. Basically whatever executes the description for real is in charge of how to do that and it could choose to change some of the assumptions like the evaluation order or the thread on which thing execute, etc.

Thus once you have Monads, you can also use them to manipulate evaluation order and semantics.

Now if you don't care about constraining yourself to pure functions, all this can seem like overkill, and it kind of is.

Obviously, people will tell you it's a good idea to make everything pure, because you can more easily test things.

And people will tell you, even if you don't care about the purity, how Monads let you manipulate evaluation order and semantics of the code can be c...

I think the List monad is easier to comprehend.
The List Monad is the same, if a function called with the same input can return one of many results, each time it is called it could return a different output. That means this function isn't pure, because a pure function maps the same input to the same output even when called repeatedly.

The difference here is instead of returning a description of the impure things that should be done, you can make the function pure by returning the list of all possible options.

That means instead of getting a different output non-deterministically for the same input each time the function is called, you now get the same output everytime, which is the list of all possible outputs for that input.

So the context is the same, you want to constrain yourself to pure functions for some reason, so how do you make this pure?

    func favorite-color(person-name)
      if(person-name.startsWith("A")
        return randOf("blue", "green")
      else
        return randOf("yellow", "black")
The trick is to have randOf return a list of all options such as

    randOf("blue", "green")
    => ["blue", "green"]
Now you have the same composition problem as before:

    func generate-favorite-color-string(favorite-color)
      return "My favorite color is: " + favorite-color("Abel")

    generate-favorite-color-string(favorite-color("Abel"))
But this won't work again, so you need a Monad once more to help you with composing these pure versions of your impure functions so that:

    bind(favorite-color("Abel"),
         generate-favorite-color-string)
Which says: compose generate-favorite-color-string with favorite-color using Monads

And now what the Monad composition will do is it will automatically run generate-favorite-color-string for each element in the list returned by favorite-color and itself will return a list of all those possible results:

    ["My favorite color is: blue",
     "My favorite color is: green"]
So the composition is also pure now.
The list monad is simple but not a representative example of monads in general. Teaching it to beginners tends to create major misconceptions like thinking all monads are collections or collection-like or "have values in".
Can someone give me a practical example of when one would want to use a monad? Typically I don't want side-effects to all live in one thing. It's cute but "we want to change the world," don't we?
Two common examples from the Ocaml world are promises through the LWT library and options.

If you just wanted to open a file, maybe you would write something like this:

    run(create_file("filename"))
Here `create_file("filename")` is a pure value, no side effects. It's a value that represents the concept of creating `"filename"` (and maybe returning a handle). When passed to `run`, that value is inspected and only then the actual action it corresponds to, creating a file, is executed.

What if you want to do something with the file? That looks like this:

    let create_then_delete = and_then(open_file("filename"), (handle) -> delete_file(handle))
    run(create_then_delete)

`create_then_delete` represents the concept of opening a file and giving it to `(handle) -> delete_file(handle)`. If you want to actually do that, you pass `create_then_delete` to `run`.

Functions with a type signature similar to `and_then` turn out to be pretty common, so it's nice to have a way to talk about them. That's basically what monads are for. In OCaml, all functions that have a similar type signature can use the new "let-monadic syntax", which basically lets you rewrite

    and_then(open_file("filename"), (handle) -> delete_file(handle))
to

    let\* handle = open_file("filename");
    delete_file(handle)
Which is a lot more readable if you ask me. Any type that has a function like `and_then` works with this syntax, and those types called monads.
If they weren't called "monads," what would be another term you think would fit the bill?
"Flatmappable"
I believe that most descriptions and blogs about monads completely miss the point.

Monads are a tool. And yes, they can be used for things like having side effects in pure code or allowing for the chaining of functions, but those are just tangential benefits. Fundamentally, monads are just a way to categorize data, and the downstream results of that data. In the end, this gives us the ability to codify what we - as programmers - already know, and therefore depend on.

Consider the following mad-lib:

> Any value computed using the result of ____ must also - by definition - be the result of ____.

Now, let's plug in a simple example with "asynchronous function":

> Any value computed using the result of an asynchronous function must also - by definition - be the result of an asynchronous function.

If I call a function call `httpGet` that returns `Future<Response>` and then I take the response and parse the HTML into a DOM object, I know - as a programmer - that the DOM object is of type `Future<DOM>`. I know this whether or not the programming language I'm using is dynamically typed, supports higher-kinded types, or not.

Whether or not you prefer to live in Haskell land, Go land, Clojure land, or Flatland, monads are just a tool for you - the programmer - to categorize your data and codify (at compile time and/or runtime) what you already know.

Flatland? great book. Surely monads are more than truisms! Forgive me, I must have missed the point.
LOL. Fair enough. :-)

I just find that when I read most people discussing "why monads" it usually degrades into code examples and the OP still left asking "yeah? so? I could already do X? what did that do for me?"

Similar to how a C++ programmer trying to tell a C programmer about std::vector might leave the C programmer is just saying "yeah? so? void* works just fine."

I was attempting to steer the discussion in a different direction: about taking what you know and codifying it, which allows you to be sure - at compile and/or runtime - about what's happening.

To take it back to your original question:

> Can someone give me a practical example of when one would want to use a monad?

Others have given good examples. But, I'll try and put it into the context of my reply by making up a contrived example.

Let's say you have a hash map and want to look up a key's value. The function can return the value stored or a default if the key doesn't exist. But, you can't tell the difference between your default value being returned or the same value already existing.

One solution would be to return multiple values, including a boolean indicating whether or not it's the default. But, presumably (since we care in this contrived example), downstream decisions and further data needs to know if the key was present. So, you need to keep passing along both values all the time. After all, per the original reply - any value derived later from the returned value also belongs to the category of "maybe derived from a default value/missing key".

Instead of constantly passing around the value, a flag, and maybe even the missing key, you could return and pass around a struct of those 3 values.

But, now you have a problem: all the existing functions you want to pass your value to don't understand your struct. Additionally, if you want to remain true to the "truism", those functions need to also return your struct, which they don't.

Monads are the pattern created for solving this exact problem. Monads define an interface for...

1. Taking a value and returning your monad type

2. Taking a function (F) and returning a new function that can take your struct, extract the original value from it, pass it to F, and return a new struct with the return value wrapped as well.

From there, you get all the other benefits people have discussed elsewhere in this thread "for free".

Whether you're working with a strongly typed language like Haskell (and using the compiler to keep you honest) or a dynamic language like Python (and keeping yourself honest at runtime), the problem and pattern holds true.

I intentionally picked a goofy, contrived example to stay clear of all the common examples people usually give (futures, IO, errors, option, etc.) to show how you might have a very different use-case that can still benefit.

Looking at 2, if F is a delivery-person and my struct is a delivery truck, then the Monad will return a new delivery-person who can take the truck, extract a parcel, pass it to the original delivery driver who will deliver it and the new delivery driver will get the signed delivery receipt, and put it in the truck. Am I getting close or just descending joyfully into madness?
Monads are not "for side effects". Monads are a programming pattern to facilitate code reuse. Any time you have a type constructor M (like List<_>, say) that supports the following operations:

1. pure :: T -> M<T>, and

2. Any one of the following three (you can derive any of these from any other)

join :: M <M<T>> -> M<T>

bind :: (M<A>, A -> M<B>) -> M<B> -- sometimes called "andThen"

pipe :: (A -> M<B>, B -> M<C>) -> (A -> M<C>)

If they interact with each other in a "sensible" way (the "monad laws"), you have a Monad.

---

What things fit this abstraction? I think the most familiar one to mainstream programmers would be Promises. If you have a Promise<Promise<A>>, you can join them together to get a Promise<A>. If you have a Promise<A> that you need to do some further work on, using the bind operation with a function (a -> Promise<B>) lets you "bind a name" to the result of the first promise and return a new Promise<B>.

If you'd like to be explicit about null-checks, then the Option<A> type lets you build up a computation without writing `if (foo != null)` everywhere. A variant, Result<E, A>, lets you stop on the first error. Tracking explicit error information in this way has been invaluable when taming a large ruby codebase - see the dry-monads project.

Any time you want to describe interactions with a system, this abstraction gives you something like the "command design pattern" on steroids. I have used Transaction<A> types to representing database transactions, ensuring that arbitrary IO doesn't happen inside a transaction (you can't unsend an email if the transaction rolls back). A function of type ((Connection, Transaction<A>) -> IO<Result<E,A>>) executes the DB operations inside a DB transaction and either returns the transaction result or an error.

None of this is "we want to change the world lol". I want to be able to notice the common elements between several type constructors, and be able to work on them using a common vocabulary, instead of reinventing wheels.

Thanks for taking the time to explain this to a novice. So are the <T>, <B>, and <C> types ? Clojure has collections like [vectors], {maps}, '(lists/seq), #{sets} and a bunch of primitives that can fill up those collections, so I'm not certain how this relates exactly... Are promises and futures always monads?
Many languages (C++ templates, C#, Java, among others) write things like List<T> to mean a "list where every item is of type T". This feature is usually called something like "generics" or "parametric polymorphism". It's not haskell notation but I hoped that it would give an easier flavour.

> Are promises and futures always monads?

Only if you can construct the necessary operations (or if they are provided), and they relate to each other correctly. For example, if you have a value m of type M<A>, (join (pure m)) should be identical to m.

The language you're using also needs to be able to usefully do something with this fact, or it's just trivia. In Haskell, we can define a type class called Monad and write operations that work on any monad, without knowing or caring which one is in use (that's the caller's job to decide). This would be hard-to-impossible to do in a less expressive language like C.

> So are the <T>, <B>, and <C> types ? Clojure has collections like [vectors], {maps}, '(lists/seq), #{sets} and a bunch of primitives that can fill up those collections, so I'm not certain how this relates exactly...

They are. It's the generic notation that you'll find in Java, C#, Rust, etc.. They are statically typed languages, so instead of just saying [vector] like in Clojure, you would say Vector<T>, where T is the type of the things in the vector. Vector<T> by itself is not a type. You can think of it as a function that takes a type and returns a type: with Int, you would get Vector<Int>, with String, you would get Vector<String>. This isn't limited to collections: Option<T> represents a value that may or may not be there, Result<R, E> represents something that can be either a result, or an error.

For the other collections, {maps} would be Map<A, B>, lists would be List<T>, sets would be Set<T>. Note that T, A or B don't mean anything specifically, they are variable names. Usually people use very abstract names for generics because they are very abstract. But you could also do List<Element> or Map<Key, Value>.

> Are promises and futures always monads?

Not all the time. For example, JavaScript promises aren't monads. There's a relatively popular discussion about it here: https://github.com/promises-aplus/promises-spec/issues/94.

As another example of a monads, there Option<T>, called Maybe<T> in Haskell. It's used for a value that could be there. Java has Optional that looks like it, but isn't a monad since it doesn't follow the monad laws: https://www.sitepoint.com/how-optional-breaks-the-monad-laws....

A good way to think about monads is to think about them as mathematical objects. They respect some rules, and thus have some properties. When you're implementing something in a language, like promises, you can choose to have them respect the monad rules, so that they can have the monad properties. You can also not do that (like they did in JavaScript), and so people won't be able to use them like monads.

But note that Set is usually not a monad. Writing the functions above is pretty easy but they'll break the rules.

In particular, unless our Set identifies its items in a way that forces a = b to imply f(a) = f(b), eliminating "duplicates" will sometimes eliminate things which can be distinguished by some function d.

This means that map(d, map(f, set)) might not always equal map(compose(d, f), set), which is required for a functor (which is more general than monad - all monads must be functors, not all functors are monads).

Some examples:

* Lists - you don't need to manually for-each over the items, the monad will do the unwrapping for you, reducing boilerplate

* Optional values - you don't need to check for null/no-value, the monad will do that for you and end the computation if an expected value is missing

* State passing - constantly passing a Context type as an argument through your pure functions? Look no further than the State monad, it will do it for you

* Environment/Configuration - don't want to deal with global variables for configuration? Look no further than the Reader monad, it will automagically carry your config to you. This can also be used as a pure dependency-injection system, with none of those hideous DI frameworks and tools.

* Asynchrony - fed up of callback functions, and the boilerplate of dealing with the continuations? Use a promise/task/async monad, it will deal with the sequencing of your operations properly. This is an interesting one because monadic-asynchrony has inspired the async/await systems in C#, JS, and beyond. So, if you've ever used async/await and thought "wow, that was convenient", it's because they're monads.

* Error handling - Exceptions are effectively gotos, why not have something a bit more powerful? The Either monad, Validation monad, etc will handle errors, groups of errors, etc.

* Output aggregation - Want to build some output over the duration of your operation (building a list of strings, generating a document, summing some values, etc.), use the Writer monad to collect output and aggregate it using any available monoid pattern.

* Parsers - Want to declaratively build a parser and not have to worry about error handling, position in the stream, etc? Get the Parsec parser-monad on the case.

* DSLs - making a DSL to capture subsystems in your code-base? Use the Free monad with a sum-type to create a ready-to-go domain-specific language. Simply provide the interpreter.

Want to combinations of all of the above? Many monads can be composed to create a super-monad with the behaviours of both; they're call monad-transformers, and everything "just works".

Ultimately, it's about capturing common patterns and using the same interface to work with the monadic types (in Haskell it's called `do` notation, in C# it's called LINQ). They significantly reduce boilerplate, and allow for very succinct and declarative code.

> Typically I don't want side-effects to all live in one thing. It's cute but "we want to change the world," don't we?

I don't really understand the argument here? Usually we want to properly separate concerns and make sure each thing has one responsibility. Monads are a big help with that.

Any time you have a secondary concern that you don't want to be dealing with explicitly in your straight-through code is a good use case. Any time you would be tempted to use aspect-oriented programming, or a global/thread-local variable, or similar hacks (and IMO they're still hacks when they're built into the language, e.g. exceptions).

E.g. authorization, audit logging, database transactions, error handling - rather than having to use some magical decorator/annotation/macro that breaks all the rules of your language, by using monads you can write code that uses these things as plain old functions working on plain old values, but there's an extensive library of standard operations (e.g. traverse, *M variants of functions like cataM) that you can use to make working with them almost as lightweight as if you were using the magic way.

As I can’t really read Clojure (there’s a few bits there I don’t understand what’s going on), can anyone elaborate on what, if anything, is Clojure specific about the examples? Or could, say, they be just as easily written in python?
This example uses macros to implement monads, Python would need a different implementation.
The author is really more accurately talking about free monads. With free monads, you make the nomadic structure clear and defer the interpretation of monastic structure for later.

Even in Haskell free monads are used pretty sparingly, so it's definitely not every day defining their own bind or return as data.

> definitely not every day defining their own bind or return as data.

I mean, thunks? But yeah, certainly not at the level we usually think of when we talk about defining data in Haskell.

But I can see labeling that an implementation issue. You need to get your dictionary to the right place somehow, and lacking the automatic type-driven threading of type classes I don't know that writing an interpreter for each is the wrong choice. For a lot of monads (reader, state, etc) you want an "interpreter" anyway. It does mean that regular values can't happen to have a relevant monad that you can use to stitch them together in a way that wasn't noticed by the person who created the value.