Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.2k views
in Technique[技术] by (71.8m points)

haskell - 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!)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

A second answer is "Don't use read".

Using read is equivalent to re-parsing data you have already parsed - so using it within a Parsec parser is a code smell. Parsing natural numbers is harmless enough, but read has different failure semantics to Parsec and it is tailored to Haskell's lexical syntax so using it for more complicated number formats is problematic.

If you don't want to go to the trouble of defining a LanguageDef and using Parsec's Token module here is a natural number parser that doesn't use read:

-- | Needs @foldl'@ from Data.List and 
-- @digitToInt@ from Data.Char.
--
positiveNatural :: Stream s m Char => ParsecT s u m Int
positiveNatural = 
    foldl' (a i -> a * 10 + digitToInt i) 0 <$> many1 digit

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...