What a Monad is not
haskell.org
haskell.org
What follows is that Monads are not containers; you don't store a value in your Monad, you store it in your List. The List just happens to be a monad. (Similarly, you can't store a ball inside your "Red", but you can store it in a "Red Box". The fact that the box is red has nothing to do with its ability to be a container. If you paint a piece of paper red, it does not suddenly become a container.)
(Side note: it happens that Monads are Functors, and Functors are logically containers... but remember, the Monad or Functor instance is not what makes something a container; something is a container because of its underlying type. "Monad" is a property possessed by certain containers, just as "Red" is a property possessed by certain other containers. Oh, and please don't make me insert a joke about Red/Black trees here...)
Put simply; Monad = adjective, not Monad = noun. It took me like two years to come to terms with this, but now that I have, Haskell is one of the easiest-to-use programming languages I know.
Thanks for this observation, however you (in your comment) and everyone else uses the word "monad" as a noun. Hence much confusion.
"The List just happens to be a monad. "
Why not say "The List just happens to be monadic", rather than saying it is a monad?
I agree with you here 100%. I was confused for years for this exact reason.
I tried avoiding the noun form for a while, but the problem is that while it's technically correct, nobody "in the know" quite understands what you are talking about. "Why didn't he just call it a Monad?"
Why not say "The List just happens to be monadic", rather than saying it is a monad?
I should have.
Altogether, that's probably for the better. If people are going to use denotational semantics to emulate a state-capabale environment, it's better if the method for doing so is standardized, well-specified, and optimized.
I am not following you here. State is nothing special, except that it is something made special by most popular programming languages. (Where it causes lots of fun problems.)
I am also not sure that "Monads" are optimized any more than any other function application. Monadic state-passing is syntax sugar more than anything else. (With other languages, state is "something else"; lexical environments, dynamic environments, and so on.)
My point was that, in practice, if pure functional langues ruled the world and did not include built-in Monads, many applications would re-invent them anyways because of their expressivity, in much the same way that C applications are said to re-invent LISP.
With very elementary design decisions, mutable state is very much manageable in the most popular languages.
Hiding state maintenance through a monad solves very little, while throwing performance out of the window. Yeah, you can get performance out of a lazy functional language, but the hoops you have to jump through are not worth it.
Ocaml is eager functional, and IMHO is a very nice compromise between expressibility and purity. Now if only its type system was as capable as haskell, and only if it had multi processor support :(
One of the reasons is precisely the "impurity" of the language. I am referring to the value restriction, which inserts `'_a` (which means "because of possible side effects, this value won't have a polymorphic type, sorry") sometimes when you do partial applications (even ones without any side effect). It bit me once when I tried to do FRP (Yampa-style) in Ocaml. I couldn't define some of the operators (time) in a modular way (in terms of integral) without losing polymorphism.
Another reason has to do with the fact that Ocaml is strict. I tried to define a mini "batteries" module with purely functional lazy lists instead of destructive streams (the enumeration). There is simply no way to define map or filter in terms of fold without losing laziness, or seriously polluting the type of fold, map, and filter. This can still be regarded as a type system issue : if strict and lazy types were compatible, as they are in Haskell, it wouldn't have been such a problem.
Monads are also not a feature of Haskell -- they can be used in any language which supports closures. I've used them in Python to great effect, though the lack of syntax support is annoying.
Please correct me if I am wrong, but all the examples you cite are about hiding mutable state using monads.
1. Non deterministic computation -> remembering last value of m and n. I assume you are talikng pseudo random generators here.
2. error handling with Maybe -> remembering last value or Nothing. Almost like passing along a single error code variable.
3. continuations -> Remember what state your computation variable and PC were last at.
If I am seeing this wrong, then I will be happy to take a second look at monads :)
If you don't understand why "monads are programmable semi-colons", take that second look. See also the list monad; the key is not the "storing in a list" but the way it does "nondeterministic computations".
This is why I also point out the list monad; fully understanding that is necessary to be sure you understand monads. I have seen numerous "monad" implementations in $YOUR_FAVORITE_LANGUAGE that can't do the list monad correctly because they can do 0 and 1 applications of a function, but can't correctly do arbitrarily many, because the people implementing the Monad interface didn't actually understand the interface. (In fact when I see such an implementation the first thing I look for now is the list monad, and so far of the four or five I've seen only one has gotten it right.)
All you've done is listed alternative ways of implementing things, which is obviously possible in a range of paradigms (albeit with semantics that are usually harder to reason about in an imperative setting).
When I say nondeterministic computation I do not mean PRNGs. I mean "give me all possible solutions to the N-queens problem". I mean "solve this sudoku puzzle with no explicit loop". Constraint solvers. Some classes of search algorithm. Proof systems. That kind of nondeterministic computation.
You'll probably respond claiming all this is state. Sure, fine, whatever makes you happy. Everything in programming is state. Everything is also lambda calls. Everything is Horn clauses. You can think about these in some very low level dungeon but that doesn't mean you ought to.
Any worthwhile type-checking algorithm (like Haskell's) would have to Scoop the Loop Snooper to accept every valid formula and deny all invalid or undecidable ones. Of course they don't actually do that, instead accepting incompleteness -- that there will be valid code that they reject.
Yes, it will work if you redefine the list constructors as functions...
(:) x xs = \f z -> f x (xs f z)
[] = \f z -> z
... and then we can define fold in a non-recursive way... but can this work in Haskell?... blog posts like this remind me why I love Tcl/Tk and shell scripting.
It is embarrassing, but I always prefer the raccoon to the panda bear.