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.
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.)
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.
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.
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?