Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I think you will find it productive to consider that you may be wrong. Your examples are literally monads (and I use the word "literally" literally). I think the problem you are facing is that your understand of what a monad is may be incorrect (and this, in turn, may be the fault of some random bad tutorial on monads).

When you are discussing things of this fashion it can be helpful to allow yourself some room to accept a view which is different than you currently hold. Otherwise it can devolve into a shouting match over who is stupider, rather than an opportunity to see things in a different way (and perhaps learn something interesting). When 2 people are arguing, the one who is right wins the argument, but the one who is wrong has the most to gain because they can learn something. By insisting on being right, you forever cut yourself off from that potential.

I know you didn't ask for that advice, so if it's not appropriate for you, please feel free to file it in the round bin.



May be. I think a Monad is an abstraction which generalizes a transition, similar to a step of logical deduction.

It Haskell a similar concept is actually used to compose what they call actions, originally used to implement IO. Out of this particular design choice, there are some specialized instances of Monads in Haskell, including State Monad.

In the context of a lazy language, like Haskell, where the order of evaluation is not defined and each expression is an implicit thunk, monadic composition is necessary and sufficient to implement serialized sequenced actions, defined as different instances of Monad type-class and to ensure, by the type checker, that these actions are of proper type (isolated from any other instances) and are properly serialized. Just this.

As a consequence of being desugared into function composition with parameter passing (>>=), which is only relevant for a lazy language, there is literally no way to access the state from outside of each pair of nested functions, so things like ST or STM are implemented as Monad.

All this is relevant for Haskell and irrelevant in Scala, where you don't have to compose functions to ensure order of evaluation. Algebraic data types and "semicolons" is good-enough.


A monad is a descriptive term for certain objects with have certain properties. For example, a ring in algebraic theory is a set together with two operations (+ and ) with certain properties. The natural numbers (1,2,3,...) together with + being addition and being multiplication form a ring, but to say that 'the natural numbers were constructed as a ring' or that 'a ring is an abstraction for the natural numbers' is wrong. Both rings and natural numbers are descriptions of things which happen to fit.

Similarly, category theory monads are a description of things with certain properties. These happen to be satisfied by IO actions in every language and Promises in JavaScript. Haskell allows you to define this abstraction within the language, but it is not the only language that allows this.


I don’t think your thinking about ordering is correct, in fact I remember reading a blog post somewhere (can’t seem to find it now) that made these same claims and was really confusing/poorly written, I’m wondering if you also read it. Anyway, monads can be about ordering but aren’t necessarily always used for this purpose, an example of a monad that doesn’t order operations is the Reader monad. I generally think about monads as generalizing composition, and not necessarily “transitions” because there are some contexts (ie Reader) where “transition” doesn’t really fit what’s happening in the computation.


> All this is relevant for Haskell and irrelevant in Scala, where you don't have to compose functions to ensure order of evaluation.

Promises and Futures are counterexamples to your claim here.


Promises and futures in Scala standard library and in Akka have nothing to do with Monads.


> I think a Monad is an abstraction which generalizes a transition, similar to a step of logical deduction.

I think this is where we're going off the rails. A monad is a specialisation of a functor (the infamous "A monad is monoid in the class of endofunctors", which I'll explain as briefly as I can later).

A functor is simply a mapping from one set to another (possibly the same set). So if I have a function that maps integers to characters, for instance this whole setup (the set of integers, the set of characters and the function that maps them from one to the other) is a functor. It gets a bit complicated when we translate that to a programming language. The set of all integers is a type. The set of all characters is a type. So we can describe the mapping from one to the other as a functor. You will probably just recognise that as a function -- and, indeed, a function is a specialisation of a functor (although the general case of a function is quite complex and I don't have anywhere enough space to explain it).

What is important to understand, though, is that the thing we are trying to map is important. We need somewhere to hold that value so that we can map it from one type to another type. Any container will do (a list, array, tuple, or even a partially applied function). When we are talking about programming with functors, we implement them using that container and a function that transforms the content from one type to another (possibly the same type).

When we map the value from one type to another, we want to put the value in a functor as well (going back to the original concept of mapping from one set to another, after we've mapped it, it is still a functor -- just one potentially holding a different typed value). Normally we implement a function called "map" (or "fmap", for "functor map") on a functor that accepts a transforming function that maps a value from one type to another. "map" then puts the result back into a functor (which is just the same kind of container you started with). As I can see you are familiar with FP languages, I'm sure you are familiar with this implementation. You have an array of integers, you can call "map" with a function that maps integers to characters and the result is an array of characters. Both the array of integers and the array of characters are functors.

Again, I'll reiterate this because it will be super important later: any container is a functor if you can map it's contents. In fact, anything that can hold state (like a partially applied function) is a functor if you can map its contents.

A monad is a specialisation of a functor. Let's go back to "A monad is a monoid in the category of endofunctors". I like that quote because it sounds like nonsense, but it's really very easy to understand if you know the definitions of the words.

A "monoid" is another specialisation of a functor. It's a functor for which there is a binary operator and an identity element. For example, let's take integers. They are monoids under addition. If we take 2 integers and add them together. There is a particular integer, "0" for which when you add anything to "0" you get the same result again. We can use addition in our mapping function (usually one of the parameters being partially applied) and if we pass it "0" we'll get the same value back.

Monoids seem a bit pointless, but they make a lot more sense when you are thinking about folding. A container isn't foldable unless is is a monoid (it's a great kata to do, if you are interested). There are lots of other implications, so I highly recommend implementing the whole hierarchy of functors from scratch to see why things are as they are.

Getting on to monads, we now know what a monoid is (not too difficult to understand). We only need to know what an "endofuctor" is. This one is also really easy to understand. An endofunctor is a functor for which the result of "map" results in exactly the same type as the original. So if you had an array of integers to start with, you end up with an array of integers. That's all it is.

"A monad is a monoid in the category of endofunctors". The last bit we have to explain is what a category is. It's just a grouping of mappings. I could have a mapping that adds 5 to an integer. I could have a mapping that adds 6 I could have one that subtracts 1. Each one of those mappings gives a result in the set of integers. As a grouping of mappings (i.e. "category"), all of these mappings describe an "endofunctor".

Finally we get to the result. In very plain terms, a monad is just a container (or anything that can hold state) which has a function that modifies the contents such that the result is guaranteed to have exactly the same type. That's all it is.

It sounds disappointingly plain, but it has surprising power. For one thing a monad holds state. Obviously this is important. But if you use the monadic mapping function (usually called "bind"), then you are guaranteed to be returned a monad of the same type. This allows you to freely chain operations without fear that something will break. This is what a monad is for.

Where I think you are getting confused is that you have seen advanced applications of monads and you have come under the mistaken impression that these are the reasons monads exist. Indeed, monads are required in a pure functional language to do those things. The nice thing about a monad is that it can hold any state for which there is an identity element. The implementation of the container is completely beside the point. As I said, you can partially apply a function and that is potentially a monad.

The even more interesting thing is that any abstract datatype can also be a monad. Where you are saying, "We don't need monads for this, we only need abstract data types", it's a bit nonsensical. These ADTs will be monads if they are used in that fashion. Indeed, objects in OOP are often monads! They just need a transforming function that returns the same type of object.

I hope you found that interesting/helpful.


Yes, it was really good read, thank you. I hope I could write as clearly.

Look, however, at the use of "is" in my comment and yours. You are applying "is" to some abstract, nonexistent categories, categorizing abstractions and naming the resulting categories. I use "is" to describe what is going on, particularly in Haskell and Scala.

The whole abstract hierarchy I regard no more meaningful as hierarchy of chakras in crya yoga texts or similar sectarian pseudo-logical but disconnected from reality teachings.

While I really appreciate that you have mastered this abstract hierarchy and could in clear and precise language explain it (which is what a mathematician supposed to do) I personally refuse to take statements like "any abstract datatype is a monad" seriously.

It is a plain type-error. You are trying to say that a triangle is a number. The correct and precise way is to say "could be viewed as", because you are literally superimpose one abstraction upon another, or view one through another, if you wish.

All these nested generalizations could be superimposed on programming, but it is fundamentally wrong to say that something in programming "is" one of these abstractions.

You are speaking like a mathematician, I am speaking like a non- (or rather anti-) Plato-Hegelian philosopher.


Hmmm... let me put it another way... When people are talking about monads, they are talking about what I was describing. :-) They use monads for what you are describing, but they also use them for many other things. The disconnect which created the conflict in the conversation was that you were using the term in a non-standard way. A person can use a word to mean anything they want, but they can't reasonably complain when other people don't understand what they are talking about.

Again, in hopes that it will be helpful to you (though I realise I'm really pushing my luck) when I discussed the issue about needing to be right, I'm referring to this kind of reply. I will try to be as modest as I can by saying that "it takes one to know one" ;-) And even my reply here is probably one bridge too far. I should probably simply understand that you are fixed to your point of view and leave it at that.

However, my goal in responding to your comments was to help you see the disconnect and to help you understand why people found your replies combative. There are definitely ways to portray your arguments such that you are in the right, but these strategies will not help you communicate with others. Western styles of communication often praise debate and the winning of arguments, but gloss over the loss of value when we look past each other.

Anyway, like I said, I've almost certainly gone too far and I don't wish to antagonise you further. You are clearly a very smart person and I think you will eventually figure it out with or without my help. If anything I've said helps you get there faster, then I'll be extremely happy. If not, then I'll apologise for any stress I may have added.

Edit: I would be remiss if I didn't point out that there are some ADTs which can not be monads. The exploration of that is extremely interesting. Since you clearly know Haskell, I recommend http://mightybyte.github.io/monad-challenges/ In challenge 1, the initial implementation of the Rand type can not be a monad. Why not? It's really interesting.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: