I don't understand how to use the lexeme funct

2019-08-08 10:42发布

问题:

From Text.Parsec.Token:

lexeme p = do { x <- p; whiteSpace; return x }

It appears that lexeme takes a parser p and delivers a parser that has the same behavior as p, except that it also skips all the trailing whitespace. Correct?

Then how come the following does not work:

constant :: Parser Int
constant = do
    digits <- many1 digit
    return (read digits)

lexConst :: Parser Int
lexConst = lexeme constant

The last line results in the following error message:

Couldn't match expected type `ParsecT
                                String () Data.Functor.Identity.Identity Int'
            with actual type `ParsecT s0 u0 m0 a0 -> ParsecT s0 u0 m0 a0'
Expected type: Parser Int
  Actual type: ParsecT s0 u0 m0 a0 -> ParsecT s0 u0 m0 a0
In the return type of a call of `lexeme'
In the expression: lexeme constant

What am I doing wrong?

回答1:

You misunderstood the documentation, the lexeme exported from Text.Parsec.Token is a field of a GenTokenParser s u m, so the type is

lexeme :: GenTokenParser s u m -> ParsecT s u m a -> ParsecT s u m a

and you haven't supplied the GenTokenParser argument in lexeme constant.

You need to create a GenTokenParser from a GenLanguageDef (typically with makeTokenParser) first to use its lexeme field.



回答2:

The lexeme function is an accessor into a GenTokenParser record of parsers generated by makeTokenParser, so you need to apply it to such a record to get at it. One common way of doing this is to use record wildcards, e.g.

{-# LANGUAGE RecordWildCards #-}

import qualified Text.Parsec.Token as Tok

Tok.TokenParser { .. } = Tok.makeTokenParser {- language definition -}

This will bring lexeme and all the other parsers into scope already applied to the record, so you can use it like you were trying to do.