Search code examples
haskellparsec

How do I get Parsec to let me call `read` :: Int?


I've got the following, which type-checks:

p_int = liftA read (many (char ' ') *> many1 digit <* many (char ' '))

Now, as the function name implies, I want it to give me an Int. But if I do this:

p_int = liftA read (many (char ' ') *> many1 digit <* many (char ' ')) :: Int

I get this type error:

Couldn't match expected type `Int' with actual type `f0 b0'
In the return type of a call of `liftA'
In the expression:
    liftA read (many (char ' ') *> many1 digit <* many (char ' ')) ::
      Int
In an equation for `p_int':
    p_int
      = liftA read (many (char ' ') *> many1 digit <* many (char ' ')) ::
          Int

Is there a simpler, cleaner way to parse integers that may have whitespace? Or a way to fix this?

Ultimately, I want this to be part of the following:

betaLine = string "BETA " *> p_int <*> p_int  <*> p_int <*>
           p_int <*> p_parallel <*> p_exposure <* eol

which is to parse lines that look like this:

BETA  6 11 5 24 -1 oiiio

So I can eventually call a BetaPair constructor which will need those values (some as Int, some as other types like [Exposure] and Parallel)

(if you're curious, this is a parser for a file format that represents, among other things, hydrogen-bonded beta-strand pairs in proteins. I have no control over the file format!)


Solution

  • p_int is a parser that produces an Int, so the type would be Parser Int or similar¹.

    p_int = liftA read (many (char ' ') *> many1 digit <* many (char ' ')) :: Parser Int
    

    Alternatively, you can type the read function, (read :: String -> Int) to tell the compiler which type the expression has.

    p_int = liftA (read :: String -> Int) (many (char ' ') *> many1 digit <* many (char ' ')) :: Int
    

    As for the cleaner ways, consider replacing many (char ' ') with spaces.

    ¹ ParsecT x y z Int, for example.