Does Go have monads ?

4,342 views
Skip to first unread message

Dave Cheney

unread,
Oct 19, 2013, 12:45:42 AM10/19/13
to golang-nuts
Hello,

In the most recent non nil able pointer discussion the concept of monads was raised.

I have tried for several years to understand what a monad is but am clearly unable to grasp the concept. So instead I'm going to ask for code.

If Go supports monads, can someone please show me what they look like in Go code.

Please don't try to explain what a monad is in a _different_ language that probably has features that Go lacks.

Cheers

Dave

andrey mirtchovski

unread,
Oct 19, 2013, 12:54:25 AM10/19/13
to Dave Cheney, golang-nuts
A monad is just a monoid in the category of endofunctors, what's the problem?

;)

Dave Cheney

unread,
Oct 19, 2013, 12:56:05 AM10/19/13
to andrey mirtchovski, golang-nuts
Congratulations on demonstrating Crockford's Law.

David Symonds

unread,
Oct 19, 2013, 12:58:26 AM10/19/13
to Dave Cheney, golang-nuts

Go doesn't have monads. The closest Go feature is perhaps either a range loop or the extinct exp/iterable package, but they are as close to monads as Go's interfaces are to C++'s virtual multiple inheritance (i.e. in that direction, but nowhere near).

Kevin Gillette

unread,
Oct 19, 2013, 1:36:37 AM10/19/13
to golan...@googlegroups.com
As I understand them, much of the motivation for monads is to provide pure functional languages that mandate object immutability with a legal way to represent objects which change (the typical example being I/O, and other interfaces to external resources which assume change). In the case of IO, for example, a file offset, in such an immutable-object language, rather than providing a read method whichchanges the offset (which would be impossible) and returns a buffer, the read method returns a copy of the file object structure, with updated buffer and offset fields; progress is made by successively accessing read-only, but mutated-from-parent copies, turtles all the way down. In this sense, it's somewhat similar to append in Go, such that the type of what it returns is identical, but the represented information is different (at least the length field is different in the copy that is returned).

The Go language itself does not have builtin support for monads, but that doesn't mean they can't be simulated as well as in any other language.

Russel Winder

unread,
Oct 19, 2013, 3:00:07 AM10/19/13
to golan...@googlegroups.com
On Fri, 2013-10-18 at 22:54 -0600, andrey mirtchovski wrote:
> A monad is just a monoid in the category of endofunctors, what's the problem?
>
> ;)

Even when being comedic, you are supposed to attribute quotes when using
them. This StackOverflow page has masses of useful material and
references, including to the original quote as well as the one that it
was derived from.

http://stackoverflow.com/questions/3870088/a-monad-is-just-a-monoid-in-the-category-of-endofunctors-whats-the-problem

On the other hand, OP did ask for an example in Go.

--
Russel.
=============================================================================
Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel...@ekiga.net
41 Buckmaster Road m: +44 7770 465 077 xmpp: rus...@winder.org.uk
London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder

James Roper

unread,
Oct 19, 2013, 3:02:06 AM10/19/13
to golan...@googlegroups.com
Hi Dave,

I know little about Go. But like you, I had no idea what monads were, until one day, after giving a presentation on promises in Play, I told someone I didn't know what a monad was, and they pointed out to that I just gave a presentation on monads. Then it all made sense. Maybe I can explain my learnings to you. Since I don't know Go, let's choose a boring language that we both know, Java. And I'll choose Java 8, because I'm typing this on my phone and monads requires passing functions.

Let's say I want to call a webservice. This webservice has two end points, a search endpoint, that given a search query returns a list of IDs. And then a details endpoint, that given an id, returns the details of the entity. So if I have a query, and I want to get the details for the entity that match that query, then I need to do two calls, the search call followed by the details call.

Now I'm going to use an asynchronous http client. Since we're in Java, we don't have all these lightweight processes or whatever it is Go provides. Instead we'll use Promises, and let's assuming I've already written a Java API that does the HTTP stuff so I can just make high level API calls. So the first call gives me back a String id, so it's going to return me a Promise<String>. This says at some point in future, a string will be available. While you might be able to call get() on it and block, that defeats the point of using an asynchronous client. So in practice you never call get, rather, you attach callbacks to it that get executed when the promise is redeemed with the string id.

So, here's our code:

Promise<String> idPromise = doSearchQuery(someQuery);

So now we want to do something with id when it's redeemed. We could attach a plain side effecting callback when it's done, but since we are functional zealots (we are aren't we?), side effects are evil. Instead, we're going to transform our promise to a promise of the details that will be returned by the details endpoint. You can easily transform a promise from one thing to another using a call called map, for example, maybe the ids are actually integers, so we we could convert it to Promise<Integer> like this:

Promise<Integer> intId = idPromise.map(id -> Integer.parseInt(id));

So now we have a promise for the id as an integer, when the id string becomes available, our callback gets executed, and then any callbacks attached to the new promise will be executed.

But in our case, because what we want to do with the id is make another asynchronous call that returns a Promise<Details>, not just Details, if we used map, we'd end up with Promise<Promise<Details>>. So instead, we use a call called flatMap, which is so called because it does a map, but then it also flattens the promise of a promise to a single promise. So the code looks like this:

Promise<Details> detailsPromise = idPromise.flatMap(id -> lookUpDetails(id));

So, when the id is redeemed, our callback to look up the details gets executed, it returns a promise, and the detailsPromise that we now have is redeemed when that promise is redeemed. And so we can then attach more map and flatMap calls, and so on.

And that is a monad. Anything with a flatMap method is a monad. Not just promises, anything that holds items of a certain type that can be transformed to another type can be a monad. A set can be a monad for example, you have a set of people, and you want a set of all their kids, if you map it you'll get a set of sets of kids, if you flatMap, you get a single set of all the kids together.

The reason why no one can explain them is they always try to explain things in the general sense, for which there is no concrete use case. But start with an implementation of a monad, such as promise, and at least for me, it's much easier to learn (or maybe you'll realise you always knew it).

So does Go have monads? If Go allows passing functions, then you could probably implement monads in Go.

Hope that helps,

James

Russel Winder

unread,
Oct 19, 2013, 3:21:25 AM10/19/13
to golan...@googlegroups.com
On Sat, 2013-10-19 at 15:56 +1100, Dave Cheney wrote:
> Congratulations on demonstrating Crockford's Law.

Do you have a reference for Crockford's Law, Google seems unaware of any
explanation of it?

> > On 19 Oct 2013, at 15:54, andrey mirtchovski <mirtc...@gmail.com> wrote:
> >
> > A monad is just a monoid in the category of endofunctors, what's the problem?
> >
> > ;)

Responder was trying to humorously point to the fact that the whole area
of monads is an explanation nightmare, witness the number of tutorials
for monads in Haskell for Haskell programmers and Clojure for Clojure
programmers. For those aware of Category Theory, monads are
straightforward, but that is not many programmers. This is sad, as
Category Theory is a good underlying theory of what software does.

On Sat, 2013-10-19 at 15:58 +1100, David Symonds wrote:
> Go doesn't have monads. The closest Go feature is perhaps either a
range
> loop or the extinct exp/iterable package, but they are as close to
monads
> as Go's interfaces are to C++'s virtual multiple inheritance (i.e. in
that
> direction, but nowhere near).

Whilst the above is true, monads are not something that has to be in the
language definition. Any language with a mechanism for passing functions
as function arguments can have monads. Languages that centre on mutable
state (shared or otherwise) do not need monads, but they can have them.
Stateless languages such as Haskell can only have a notion of sequence
and side-effects (e.g. non-terminal output) using monads.

A number of people have shown monads in Java and C++. I think it would
be interesting to see if there is a good implementation possible in Go,
and will attempt this. However, given it has to be a hobby project, it
may take a while. As soon as I have something, I'll put it up on GitHub
or BitBucket and let people know it's there for them to dissect.

Of course someone may beat me to it…

Rémy Oudompheng

unread,
Oct 19, 2013, 3:48:45 AM10/19/13
to Dave Cheney, golang-nuts
A monad is a particular structure built out of parametric types and
associated generic functions. This is not something you can express in
Go.

Rémy.

Dave Cheney

unread,
Oct 19, 2013, 3:58:23 AM10/19/13
to Russel Winder, golang-nuts
> Do you have a reference for Crockford's Law, Google seems unaware of any
> explanation of it?

http://youtu.be/dkZFtimgAcM?t=45s

Dave Cheney

unread,
Oct 19, 2013, 4:01:25 AM10/19/13
to Russel Winder, golang-nuts
> A number of people have shown monads in Java and C++. I think it would
> be interesting to see if there is a good implementation possible in Go,
> and will attempt this. However, given it has to be a hobby project, it
> may take a while. As soon as I have something, I'll put it up on GitHub
> or BitBucket and let people know it's there for them to dissect.

As I understand it there is not one monad, but a (infinite?) set of
them, of which most people can only recall the IO and Maybe monad.

I would be extremely interested in seeing any monad implementations in
Go, even if they where unwieldy or flawed.

Cheers

Dave
Message has been deleted

roger peppe

unread,
Oct 19, 2013, 5:43:11 AM10/19/13
to Paul Hankin, golang-nuts, Dave Cheney
The question isn't really "does go *have* monads?", but "can one implement
monads in Go?"

The answer is, yes, kind of, but the general form of monads is
fairly useless in Go. On the other hand, the general idea is still
very useful.

In their most general form, monads are simply a framework for
expressing a *sequence of operations* such that each operation
in the sequence can be subject to some higher level control.

In the classic case of the IO monad, the higher level control
is simply "run this stuff with access to results from IO operations".

Here's a simple example of this technique in Go; I've tried
to keep fairly close to the original Haskell ideas:

http://play.golang.org/p/GtbiasioCG

Note that there are many things that can be enforced by the compiler
in Haskell that cannot in Go. Haskell can enforce that you can't
mix different Monad implementations - it can also keep track of
the actual value returned by a function and check the type.
I've omitted error checking because it's tedious to bundle
tuples into an interface in Go.

That's a very simple example - it doesn't really show IO-derived
values being used. Here's the same example vamped up a little - we
can open files and read them piece by piece.

http://play.golang.org/p/d0bPJKyKEc

Note the way that it's not possible to use a straightforward
loop to read all of the file contents - we need to use recursion,
because with monads each step in an IO sequence is defined
inside the scope of the previous step - we can't use a value
derived from an earlier step without being inside a closure.

Here's another classic example of monad use - for errors.

http://play.golang.org/p/9fZ467PC_c

Again, it's incredibly awkward in Go. (It would actually be very
awkward even in Haskell without some syntactic support).
The basic idea is that each step in the sequence can only proceed
if all previous steps have succeeded.

Note how the error returned does not describe *which* step failed;
that's a problem with this approach in general - it assumes that an unadorned
error is sufficient diagnostic context. Remember that, the next
time someone suggests that the Haskell Either monad is a better
approach than Go's explicit error checking.

To close on a more upbeat note - Gustavo Niemeyer's package
http://godoc.org/labix.org/v2/pipe could actually be considered
a more idiomatic approach to using a monadic style in Go.

It's domain-specific, yes, but in their general form, monads really don't give
you any value that's not trivially implemented.

The basic approach, though, where you can combine user-level
functions with higher-level control flow, is very useful.
Note in particular that none of the Pipe methods return errors - all
the error checking is done at the higher level. The user can
specify the structure of the pipe without needing to bother
about all the tedium of building it.

Hope this helps a bit.

rog.

[Well, that made a couple of hours at the airport fly by!
See ya in SF, Dave...]

On 19 October 2013 09:03, Paul Hankin <paul....@gmail.com> wrote:
> I disagree. You can't write the generic monad infrastructure in Go, but for
> sure you can write concrete monads.
>
> For example, here's a "Maybe int" monad.
>
> type MaybeInt struct {
> bool ok
> x int
> }
>
> func (i MaybeInt) Bind(func f(i int) MaybeInt)) MaybeInt {
> if !i.ok {
> return i
> }
> return f(i.x)
> }
>
> func Return(i int) MaybeInt {
> return MaybeInt{true, i}
> }
>
> Why anyone would consider this a good idea is a different question.
>
> --
> Paul
>
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.

Dave Cheney

unread,
Oct 19, 2013, 5:52:56 AM10/19/13
to roger peppe, Paul Hankin, golang-nuts
Thanks Rog. I'm glad I could keep you occupied ;)

Russel Winder

unread,
Oct 19, 2013, 6:20:54 AM10/19/13
to Dave Cheney, golang-nuts
On Sat, 2013-10-19 at 19:01 +1100, Dave Cheney wrote:
[…]
> As I understand it there is not one monad, but a (infinite?) set of
> them, of which most people can only recall the IO and Maybe monad.
>
> I would be extremely interested in seeing any monad implementations in
> Go, even if they where unwieldy or flawed.

Looks like Roger already provided the needful!

Patrick Higgins

unread,
Oct 19, 2013, 8:44:56 AM10/19/13
to golan...@googlegroups.com
I can't believe this entire thread has proceeded without mentioning the obvious name for a package implementing monads for go!

Jan Mercl

unread,
Oct 19, 2013, 9:08:23 AM10/19/13
to Patrick Higgins, golang-nuts
On Sat, Oct 19, 2013 at 2:44 PM, Patrick Higgins
<patrick.al...@gmail.com> wrote:
> I can't believe this entire thread has proceeded without mentioning the obvious name for a package implementing monads for go!

omg ;-)

-j

Oleku Konko

unread,
Oct 19, 2013, 9:47:54 AM10/19/13
to golan...@googlegroups.com, Russel Winder
lol ... Nice link thank you

Russel Winder

unread,
Oct 19, 2013, 10:08:31 AM10/19/13
to golan...@googlegroups.com
On Sat, 2013-10-19 at 05:44 -0700, Patrick Higgins wrote:
> I can't believe this entire thread has proceeded without mentioning the obvious name for a package implementing monads for go!

Did you not look at the Douglas Crockford keynote film? Especially the
title.

:-)

Patrick Higgins

unread,
Oct 19, 2013, 10:11:28 AM10/19/13
to Russel Winder, golang-nuts

Oops, missed that. Doug didn't realize the extra special meaning it would have for this group.

--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/iIAhrtK8XRo/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.

atomly

unread,
Oct 23, 2013, 10:21:41 AM10/23/13
to roger peppe, Paul Hankin, golang-nuts, Dave Cheney
On Sat, Oct 19, 2013 at 5:43 AM, roger peppe <rogp...@gmail.com> wrote:
Note how the error returned does not describe *which* step failed;
that's a problem with this approach in general - it assumes that an unadorned
error is sufficient diagnostic context. Remember that, the next
time someone suggests that the Haskell Either monad is a better
approach than Go's explicit error checking.

I can't help but chime in here-- Haskell does have much better error handling than just using the Either monad, e.g. http://hackage.haskell.org/package/mtl-1.1.0.2/docs/Control-Monad-Error.html

Also, there are things like Validations (which aren't really monads, but rather applicative functors) that can be terribly useful.

:: atomly ::

[ ato...@atomly.com : www.atomly.com  : http://blog.atomly.com/ ...
[ atomiq records : new york city : +1.347.692.8661 ...
[ e-mail atomly-new...@atomly.com for atomly info and updates ...

davidth...@gmail.com

unread,
Oct 23, 2013, 10:01:01 PM10/23/13
to golan...@googlegroups.com

roger peppe

unread,
Oct 24, 2013, 1:28:41 AM10/24/13
to atomly, Paul Hankin, golang-nuts, Dave Cheney
On 23 October 2013 07:21, atomly <ato...@gmail.com> wrote:
> On Sat, Oct 19, 2013 at 5:43 AM, roger peppe <rogp...@gmail.com> wrote:
>>
>> Note how the error returned does not describe *which* step failed;
>> that's a problem with this approach in general - it assumes that an
>> unadorned
>> error is sufficient diagnostic context. Remember that, the next
>> time someone suggests that the Haskell Either monad is a better
>> approach than Go's explicit error checking.
>
>
> I can't help but chime in here-- Haskell does have much better error
> handling than just using the Either monad, e.g.
> http://hackage.haskell.org/package/mtl-1.1.0.2/docs/Control-Monad-Error.html

Isn't that just an example of adding an error monad to the IO monad
(with the error monad still being based on Either)?

It's certainly difficult to get a handle on just how the Haskell
approach might be an improvement on Go's when the example functions are just
as long as if they had been written Go-style with "if err != nil..."
conditions.

Monad transformers were one of the things that I never
managed to get my head around in my few months of
using Haskell.

Rémy Oudompheng

unread,
Oct 24, 2013, 1:56:24 AM10/24/13
to roger peppe, atomly, Paul Hankin, golang-nuts, Dave Cheney
2013/10/24 roger peppe <rogp...@gmail.com>:
> On 23 October 2013 07:21, atomly <ato...@gmail.com> wrote:
>> On Sat, Oct 19, 2013 at 5:43 AM, roger peppe <rogp...@gmail.com> wrote:
>>>
>>> Note how the error returned does not describe *which* step failed;
>>> that's a problem with this approach in general - it assumes that an
>>> unadorned
>>> error is sufficient diagnostic context. Remember that, the next
>>> time someone suggests that the Haskell Either monad is a better
>>> approach than Go's explicit error checking.
>>
>>
>> I can't help but chime in here-- Haskell does have much better error
>> handling than just using the Either monad, e.g.
>> http://hackage.haskell.org/package/mtl-1.1.0.2/docs/Control-Monad-Error.html
>
> Isn't that just an example of adding an error monad to the IO monad
> (with the error monad still being based on Either)?
>
> It's certainly difficult to get a handle on just how the Haskell
> approach might be an improvement on Go's when the example functions are just
> as long as if they had been written Go-style with "if err != nil..."
> conditions.
>
> Monad transformers were one of the things that I never
> managed to get my head around in my few months of
> using Haskell.
>

A monad transformer is a way of "composing" monads : given two monads M and T
MT will also be a monad whenever you define a function T(M a) -> M(T a)
(because you will obtain a function MTMT(a) -> MMTT(a) -> MT(a) for
the monad structure).
For example: a state with a list of foobars (state, [x1 ... xn]) can
be converted to a list of stateful foobars: [(s,x1) ... (s,xn)], and
this is a concrete incarnation of the transforming properties of the
state monad.

It is possible in many cases and allows you to combine interesting
library features when they are expressed as monads (like stream
processing, stateful computations, etc.). The resulting pain depends
on how complicated the monads are or how inappropriate the APIs are.

Rémy.

Bienlein

unread,
Oct 24, 2013, 4:13:44 AM10/24/13
to golan...@googlegroups.com
Thanks James for your effort to explain monads. I don't feel like I understand the whole thing, but that cannot be gained without taking a dedicated effort, I guess. Now I also better understand flatMap in Scala. One thing I don't get with Promises is what happens when the moment of truth has come. The exchange is closing down in 5 minutes and all those deals need to carried out physically now. So you call get on your promise that holds on to another promise, etc. Till everything has been computed the exchange has closed their doors already... Or am I getting something wrong here with those promises?

@atomly: This is now the 2nd time within some months that I visited your homepage and it still doesn't work ... ;-)

-- Bienlein

atomly

unread,
Oct 24, 2013, 11:29:40 AM10/24/13
to roger peppe, Paul Hankin, golang-nuts, Dave Cheney
I think Remy did a decent job explaining monad transformers, which are, in fact, terribly useful.  

At my work, for example, the core of product is a monad that takes care of our database/redis/metrics/logging/etc.. It's great because it allows us to chain our operations together using scala's monadic for.  I would say it's somewhat similar to this scala example or this haskell example, but with even more power because we include more than just our DB stuff. 

Basically it's a different way to do IOC that I feel is much cleaner.

:: atomly ::

[ ato...@atomly.com : www.atomly.com  : http://blog.atomly.com/ ...
[ atomiq records : new york city : +1.347.692.8661 ...
[ e-mail atomly-new...@atomly.com for atomly info and updates ...


David Thomas

unread,
Oct 24, 2013, 7:51:28 PM10/24/13
to golan...@googlegroups.com
Okay, I decided to play with monads in Go a little bit more. Here's how to make functions that work in a generic monad. It's not compile-time typesafe, but that's standard fare for Go since it doesn't have generics.


For those who care, I'm pretty sure this uses a pattern that encodes type classes, albeit one that requires passing the type class instance explicitly (hence the Monad argument to the functions passed to the Bind method). See http://ropas.snu.ac.kr/~bruno/papers/TypeClasses.pdf, where this same encoding is referred to as the Concept Pattern. The paper discusses it from Scala's perspective, though, where you can get around having to pass the instance explicitly using implicits.

What do you think? This is actually not that bad! Monads can be used in Go, and this example even shows how it can be used for abstraction. There are likely better ways to accomplish this kind of thing in Go, ways that allow more compile-time type-checking for instance. But no one can claim that it can't be done. However, syntactic support for monads, like Haskell's "do" and Scala's "for", help a lot, and you definitely don't have that in Go.

Rob Pike

unread,
Oct 24, 2013, 8:23:33 PM10/24/13
to David Thomas, golan...@googlegroups.com
I am reminded of Dr. Johnson's dog.

-rob

David Leimbach

unread,
Oct 27, 2013, 12:27:39 PM10/27/13
to golan...@googlegroups.com
This stuff, while neat, is not often about building the monad but wanting to have an environment of execution with different guarantees than the base languages provides.  This boils down to continuation passing.

Executing a script inside a Maybe-like environment gives you at least the inability to ignore failures by default.  I have wanted this for Go myself.  With if blocks in Go we get opt-in error handling.  A Maybe environment would not be able to opt-in for failure tracking, but you might be able to opt out.

I posit that what people really want to do with monads is actually a form of CPS, since the monadic bind is what people are interested in and it uses continuations to do its work.  The semicolon of a monad (>>=) threads continuations between the binding operations.

Anecdotally, a friend of mine got to review The Reasoned Schemer before it was released, which built out a fully monadic logic language inside scheme.  I remember, perhaps incorrectly, that the authors of that book arrived at the conclusion that all the monadic stuff was a bit of overkill and that this logic language could have been done more cleanly in a CPS style.

WWPWD?   (What Would Philip Wadler Do)

Go allows you to write CPS style stuff.  I think that's enough :-).  

Dave

stick...@gmail.com

unread,
Dec 20, 2013, 5:44:45 AM12/20/13
to golan...@googlegroups.com
Sorry to bring up an old thread, but someone on twitter directed me to here.

Essentially it's not that much of a pain to implement a basic set of Monads (transformers might take some work!) in GO. Although not everything is baked and there are a lot of rough edges, including implementing Monoid pure and empty, which do need a lot of thought in to it.

In response to Dave:


"I would be extremely interested in seeing any monad implementations in
Go, even if they where unwieldy or flawed."

https://github.com/SimonRichardson/wishful

It is flawed, but hopefully not unwieldy and surprisingly fun thing to try in a language which I've never had a chance to learn until yesterday (nice syntax by the way!). As for the motivation, hell why not! ;-)

Cheers
Si
Reply all
Reply to author
Forward
0 new messages