Why monads have not taken the Common Lisp world by storm (2008)
marijnhaverbeke.nl
marijnhaverbeke.nl
Instead, I had to figure out why I was having such a hard time coming up with a satisfactory implementation of the pattern in C# - and why I maybe shouldn't have wanted one in the first place - the hard way.
In the simpler cases where use of monads can be replaced by simple mutable state -- you still lose out on the explicit types of the mutating vs. pure code.
For example, STM is possible because mutating effects are typed, and so can be ruled out of STM transactions.
And in the more complex monads (e.g: transformer stacks), mutable state is just not good enough. You can compose transformers to build things that mutable state simply cannot express, and you'd have to CPS transform your code and avoid mutable state to express those things.
For example, these two monads:
ListT (ParsecT m)
ParsecT (ListT m)
Have no corresponding "mutable state" representations.On Lisp has a chapter devoted to making a leaky macro-based CPS transformer for Common List. ContT adds CPS to any monad stack as quickly as
newtype ContT r m a = ContT { runContT :: (a -> m r) -> m r }This is really a point that seems to get lost in most of learning materials on the web. LYAH doesn't even acknowledge transformers. I am pretty new to Haskell but it seems to me that transformer stack decisions are a pretty significant design decision for your application. What has happened to me is almost all the code in my application has wound up inside this stack; the only pure functions are trivial helpers. I'm not aware of what has been written on this topic in terms of guidance or advice. But it seems like it is definitely one of the later-stage humps for a new Haskell developer like me.
The solution is usually to use the mtl-style transformer classes which let you write generalized functions which depend upon capabilities of the transformer stack instead of the concrete stack itself. You end up with a sort of natural dependency injection framework. For instance, here's a function which works on any RW-style monad stack
augment :: (MonadReader a m, MonadWriter a m) => a -> m ()
augment a = ask >>= \a' -> tell (a' <> a)
where the Monoid constraint allowing us to use (<>) is coming from the typeclass Prolog involved with the MonadWriter class.You can then use augment in ANY monad transformer stack which involves Reader and Writer over the same state type.
Note that this defers the extremely important decision about your monad transformer layer order---the caller of augment decides whether to call it with "ReaderT w (Writer w) a" or "WriterT w (Reader w) a". Reader/Writer commute so it's not a problem, but this changes things in the op's example using "ParsecT (LogicT m)" versus "LogicT (ParsecT m)". If your function depends on a particular ordering of the effects then it must have a less general type.
getAny :: (Random a) => State StdGen a
getAny = do g <- get
(x,g') <- return $ random g
put g'
return x
I was like.. how on Earth Haskell knows which function "get" should it call? I think this is a point which should be more stressed in the tutorials (I read Learn yourself a Haskell and glanced at couple of others..)Of course, when it fails you know immediately and can remedy it by type annotations, but that can still be challenging.
To answer the question, `get` has its principle type resolved by the type inference engine. In this case, `get`'s most general type is `MonadState s m => m s` and it can resolve what `m` is by unifying it with the type annotation of `getAny`, so we know that we need `get :: MonadState s (State StdGen) => State StdGen s`. From here, the compiler searches through the type class instances in a Prolog-like style to find that `MonadState s (State StdGen)` occurs when `s ~ StdGen`. This resolves more type information and also tells us which definition of `get` is needed—the one that was defined as `instance MonadState s (State s) where`!
The end result is that get ends up with the type `get :: State StdGen StdGen` and is the function defined as `get = State $ \s -> (s, s)`.
getAny :: (Random a) => State StdGen a
getAny = do g <- ?whatAmI get
(x,g') <- return $ random g
put g'
return x
which will fail the type checker because your type didn't include the information about what ?whatAmI is---the error will be something like "could not find definition of ?whatAmI :: State StdGen StdGen -> State StdGen StdGen", although you may only get partial information as the compiler may complain about your undefined function before it's resolved what you're looking for.You can also leave off the type annotation and GHCi will infer the type of ?whatAmI. Then if you check the type of "getAny" you'll see
getAny
:: (?whatAmI::m s -> n s', RandomGen s', Random b,
MonadState s m, MonadState s' n) =>
m b
which provides all of the information the typechecker knows all in one go. Take note that the type it derives is somewhat more general than the one we want since it allows ?whatAmI to transform the involved monad. Obviously "get" doesn't do this. * Try to quickly write the whole thing as a single recursive descent parser. Note the exploding amount of ugliness. Give up.
* Separate out the tokenizer (novel idea, huh?) to keep parser complexity down. Parser is still a mess. Ugh!
* Play around with some CL parser frameworks. This helps a bit, but none of the systems I tried produce errors with enough information.
* Remember the breeze it was to write a parser with the Haskell Parsec library. Mess around with monads for a while, learn a few things, but not how to write elegant parsers in Common Lisp.
I've been going through these exact steps but in JavaScript. Writing languages is fun! And makes me want to kill things!I came across this* article, which uses JavaScript as the implementation language, and found it very interesting, in large part because this approach (Top down operator precedence) sort of pulls the precedence hierarchy out of the call graph of a recursive descent parser and into a table, but also because it's an approach that OOP (and JavaScript in particular) is well suited to. I've used it (in combination with traditional recursive descent) in a functional setting (Standard ML) as well, and would use it again, especially for parsing infix expressions (arithmetic expressions, type annotation expressions).
This is a bit of a tangent, but I thought you might be interested given the intersection of parsing and JavaScript. I've been meaning to write this up in a short blog post...
* http://javascript.crockford.com/tdop/tdop.html There's another article on this using Java as the implementation language: http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-e...
[1]http://learnyouahaskell.com/functors-applicative-functors-an...
Oh yeah, incredibly ugly \s
OP, you may be interested in checking out LiL, the Lisp interface Library.
(funcall (function variable) arg1 arg2 ... argn)
Or shorthand (funcall #'variable arg1 arg2 ... argn)
Basically, some people find this ugly, I mostly find it to say "HEY!! We're doing this specific thing right here". To me it's more helpful than ugly.