Haskell problem

Tom Pledger Tom.Pledger@peace.com
Thu, 21 Feb 2002 14:16:43 +1300


Mark Wotton writes:
 | Hi,
 | 
 | I'm trying out some combinatorial parsers, and I ran into a slightly 
 | inelegant construction. To parse a sequence of things, we have a function 
 | like
 | 
 | pThen3 :: (a->b->c->d) -> Parser a -> Parser b -> Parser c -> Parser d
 | pThen3 combine p1 p2 p3 toks =
 |         [(combine v1 v2 v3, toks3) | (v1, toks1) <- p1 toks,
 |                                      (v2, toks2) <- p2 toks1,
 |                                      (v3, toks3) <- p3 toks2]
 | 
 | The problem with this is that this structure has to be duplicated for
 | pThen2, pThen4, and so on. These other forms are very similar to pThen3,
 | but there seems to be no way to capture this in Haskell's type system, as
 | the combine function has a different signature for each pThenX. (This is
 | actually the first time the Haskell type system has got in my way rather
 | than helping.) Is there a way around this problem?

If you can build the list of tokens into the state of your Parser
monad - which would include making  Parser a  a distinct type instead
of an alias for  [Token] -> [(a, Token)]  - you could condense pThen3
down to this:

    pThen3 combine p1 p2 p3 toks
        = do v1 <- p1
             v2 <- p2
             v3 <- p3
             return (combine v1 v2 v3)

which is equivalent to the liftM3 function from the Monad library.
You'd still be stuck with a family of lifting functions, but at least
they're predefined.  I don't see any way of getting by with a fixed
number of lifting functions, unless you're willing to clutter the call
sites:

    infixl 0 `ap`
    ap :: Parser (a -> b) -> Parser a -> Parser b
    ap = Monad.ap
    ...
    ... Monad.liftM f p1 `ap` p2 `ap` p3 `ap` p4

Regards,
Tom