"It appears that in the presence of mutable state, a lot of the advantages of monads become moot."
If you have imperative state then toy uses of the state monad might be overkill. But, for example, in a parser, imperative state is unlikely to help you. Here, you want state to be discarded at failure and old states restored by backtracking. The execution paths are highly complicated (as they always are when you are working with what are effectively continuations), and trying to reason about these paths in the face of imperative update isn't easy, and is likely to lead to weird bugs.
Toy examples are not generally going to help you appreciate monads, and I think there's a fourth requirement to make them work in a language: you need a powerful type-system. I'd say that monads haven't taken F# by storm because F# isn't powerful enough to type-check a library for arbitrary monads. And you want that, because when it comes to reasoning about code using such libraries and then applying it to a monad assembled from a stack of several other monads, you really want your type-checker around.
Ocaml's type-system is powerful enough for this, and I still reach for my monad library when writing Ocaml code. It's syntactically heavier than Haskell, but not too painful. The solution is to make all your monads into modules, which would be a bit like making every monad a dictionary and passing it around explicitly. This is basically what Haskell ends up doing with type-classes anyway, and what Gabriel Gonzalez has suggested one do explicitly:
If you follow this approach, Common Lisp and any other language can pull it off by making all monads into dictionaries. All you lose is the type-checking.
By "imperative state" I mean state for which the ordering of state updates is set by the operational semantics of your language, or the execution model, or the way it ends up implemented on an abstract machine. In the case of Haskell, I mean the ordering of state updates you'd get if you used unsafePerformIO whenever you do your get, put and modify, which is generally going to be unpredictable.
If I'm working in code where the operational semantics is not fully defined or difficult to reason about, I might find myself reaching for the state monad, regardless of whether my language supports imperative state (such as with Ocaml). I brought up back-tracking parsers as an example.
I don't know who you've been talking to that considers Haskell to be an imperative language, but that's nonsense. Haskell is about as far from multi-paradigm as you can get. Do-notation is nothing but syntactic sugar for regular monadic expressions, which are pure functional in every way.
Merely quoting Simon Peyton Jones and Philip Wadler in their paper from 1993 "Imperative Functional Programming" [1].
Chaining monadic binds with a `do` is the textbook example of imperative programming (it's actually the very reason why the keyword 'do' was chosen for syntactic sugar, because it captures perfectly the imperative aspect: "do this and in this order").
You seem to think it's a bad word but I really don't understand that. Haskell is both functional and imperative, there's really nothing wrong about it.
Huh. We clearly mean different things by "imperative programming". To me, sequential computation is a necessary but really-far-from-sufficient aspect of imperative programming. A monadic expression is still an expression, not a statement. It is still referentially transparent. I don't know how you can look at a referentially transparent expression and call it imperative other than by being very liberal with your definitions.
Because monads allow you to basically embed "Algol" into Haskell. It's super imperative, despite it being interpreted as a pure function.
I'd actually go so far as to say Haskell is quite multi-paradigm but only the FP side comes close enough to Haskell's "purity" as to make a community translation. I'd be quite willing to say that Haskell has a fantastically correct if slightly difficult to use object system.
It just doesn't quack anything at all like Smalltalk or Java's.
I strongly disagree, but am sure that that disagreement comes only from us defining terms differently. If you define the term "imperative" widely enough to include Haskell, what language does it not include?
That still sounds a little weak, but even under that definition, how does Haskell qualify? Do-notation may make things look like statements, but they are still expressions!
They are statements in an embedded language modeled by the monad. The distinction isn't great. Monad allows the implementer to capture linear action if they like. That makes them at least powerful enough to embed Algol.
a tangent on the example of parsers that happened to be used: I found imperative mutable state made experimenting with different kinds of parsing algorithms tremendously easier. You can just say what you mean. Now, I'm sure there is a way to express it with monads (that would be simpler to reason about and confirm for correctness etc), but the first problem for me, in experimenting with different approaches, was to get my ideas out of my head and into code, and get them to work at all.
If I was more skilled with monads, I might go directly to the ideal monadic solution... but I suspect I wouldn't have ideas that didn't fit well with monads in the first place. That is, (I fear) thinking in monads would restrict the breath of approaches that would come to mind... Disclaimer: because I'm not skilled in monads, I don't really know.
Monads make you be more clear than you might otherwise. Saying what you mean just means that you're used to the semantic space of your "standard imperative".
This comes to play in parsers because a lot of their challenge is in properly piping around the recursive-backtracking state parts. Using monads you just create a semantic world where this recursive backtracking bit is built into the "nature" of state in your particular monad.
Author here. I am considering putting a "this is ancient, please don't submit it to social linking sites" header on top this. It's appeared on here two times in the past year. It never was a very good article to begin with, and six years later, it just looks lame.
For clojure, there's kern (https://github.com/blancas/kern) for parsec style parser combinators. It's quite easy to use. Ironically, I actually understood parser combinators better by reading its wiki (By understood, I mean understood how to _use_ parser combinators). Parsec was initially difficult to grasp because the type system complicated things for me then.
I only got Parsec completely after that eureka moment when I finally understood Monads.
Why all this hate about Clojure? I didn't downvote you, but I can see why people would, seems olenhad was mentioning a library similar to Parsec in a language that is very related to Common Lisp. There is definitely no need to get carried away.
I am honestly curious what you have against Clojure. I dabbed with CL and Clojure, not as much as I would have liked to, but for me they felt similar. I would be curious to have a list of things that can easily be achieved with CL but not with Clojure.
Common Lisp is a multi-paradigm language (imperative, functional, object-oriented) in the Lisp 1 / Maclisp tradition. It has a language spec and multiple different, but very compatible, implementations. Among its goals are portability across different machines/systems, efficiency, power and stability. Thus complex Lisp software can be ported or written in Common Lisp and usually runs on top of several implementations (examples: Maxima, Axiom, ACL2, ACT-R, Common Lisp Music, ...).
"Clojure is predominantly a functional programming language, and features a rich set of immutable, persistent data structures. When mutable state is needed, Clojure offers a software transactional memory system and reactive Agent system that ensure clean, correct, multithreaded designs."
Things can be both incompatible and related. For any given thing, the set of things related to that thing will typically be bigger than and encompassing of the set of things compatible with that thing.
I guess I don't really get your point. Is Clojure not a pure enough Lisp for your taste, or what? I've used Clojure and CL a similar (small) amount, and they seem incredibly more similar to each other than either of them to Fortran.
For me languages which are fully incompatible and allow no code sharing are not VERY related. Languages VERY related to CL are Emacs Lisp, ISLisp and some other Lisps in the Lisp 1 / Maclisp tradition.
More food for the downvoters: Clojure has been carefully designed such that ZERO code sharing with any other Lisp dialect is possible. That's its definition of 'very related'.
From watching half of it, he mistakes monads for being a trick to avoid Io, which is not true. Io really does happen in Haskell, it is just contained. Also he mentions types not being necessary to understand monads, but immediately launches into a description of the bind and unit functions by, you guessed it, stating what the types of the function's were and what kinds of things they returned (I.e. the exact thing he said he wouldn't talk about on the previous slide).
That being said, I liked his reworking of bind to be a method on an object. I agree monadic computation is tedious to read when you don't have an infix bind operator, and his trick of turning bind into a method solves that pretty well in a language like JavaScript.
I think he's probably overstating the case that what he calls ajax chaining is always monadic (but what most of us who didn't write a competing library would call jquery style method chaining). I would guess most implementations of this kind of APIs do not adhere to the monad laws.
I'm not interested in watching it again, but my impression from the last time is that he's about 3/5ths of the way to "true monad nirvana" if such a thing were a place. There are higher levels above that where we start to really understand the category theoretic and PLT theoretic mechanism of monads, but I think that's truly a different tier.
The reason why they have not taken CL… wait, for starters why must they? Haskell is far far more advanced in the ideas of abstaction (strong static typing, — so it just needs to be). Experienced lispers will put yet another DSL, experienced haskellers won't, for “it's just monoid in the category of endofunctors, duh” (haskell forklore)
Let me guess, the 'parser for a reasonably complicated language' was actually a Javascript parser written in CL, more exactly what is today http://marijnhaverbeke.nl/parse-js/ ?
MPC is a monadic parser combinators library. It provides a toolbox of predefined parsers, facilities for error handling and parses arrays, streams and lists.
There's cl-parser-combinators (https://github.com/Ramarren/cl-parser-combinators), which is supposed to aid in writing parsers in a painless, Parsec-like way. I haven't used it, but it may be worth a try.
If you have imperative state then toy uses of the state monad might be overkill. But, for example, in a parser, imperative state is unlikely to help you. Here, you want state to be discarded at failure and old states restored by backtracking. The execution paths are highly complicated (as they always are when you are working with what are effectively continuations), and trying to reason about these paths in the face of imperative update isn't easy, and is likely to lead to weird bugs.
Toy examples are not generally going to help you appreciate monads, and I think there's a fourth requirement to make them work in a language: you need a powerful type-system. I'd say that monads haven't taken F# by storm because F# isn't powerful enough to type-check a library for arbitrary monads. And you want that, because when it comes to reasoning about code using such libraries and then applying it to a monad assembled from a stack of several other monads, you really want your type-checker around.
Ocaml's type-system is powerful enough for this, and I still reach for my monad library when writing Ocaml code. It's syntactically heavier than Haskell, but not too painful. The solution is to make all your monads into modules, which would be a bit like making every monad a dictionary and passing it around explicitly. This is basically what Haskell ends up doing with type-classes anyway, and what Gabriel Gonzalez has suggested one do explicitly:
http://www.haskellforall.com/2012/05/scrap-your-type-classes...
If you follow this approach, Common Lisp and any other language can pull it off by making all monads into dictionaries. All you lose is the type-checking.