如何让Parsec让我调用`read` :: Int?

时间:2012-05-23 18:51:19

标签: haskell parsec

我有以下类型检查:

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

现在,正如函数名所暗示的那样,我希望它能给我一个Int。但如果我这样做:

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

我收到此类错误:

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

是否有更简单,更简洁的方法来解析可能有空格的整数?或者解决这个问题的方法?

最终,我希望这成为以下内容的一部分:

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

用于解析看起来像这样的行:

BETA  6 11 5 24 -1 oiiio

所以我最终可以调用一个需要这些值的BetaPair构造函数(一些是Int,一些像其他类型,如[Exposure]和Parallel)

(如果你很好奇,这是一个文件格式的解析器,其中包含蛋白质中与氢键合的β链对。我无法控制文件格式!)

3 个答案:

答案 0 :(得分:7)

  

如何让Parsec让我拨打read :: Int

第二个答案是“不要使用阅读”。

使用read相当于重新解析已经解析过的数据 - 因此在Parsec解析器中使用它是一种代码味道。解析自然数是无害的,但是read与Parsec有不同的失败语义,它适合Haskell的词法语法,因此将它用于更复杂的数字格式是有问题的。

如果您不想解决定义LanguageDef和使用Parsec的Token模块的麻烦,这里是一个不使用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

答案 1 :(得分:5)

p_int是一个生成Int的解析器,因此类型为Parser Int或类似的¹。

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

或者,您可以键入read函数(read :: String -> Int)来告诉编译器表达式的类型。

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

至于更清洁的方式,请考虑将many (char ' ')替换为spaces

例如,

¹ParsecT x y z Int

答案 2 :(得分:1)

你可能会找到

Text-Megaparsec-Lexer.integer :: MonadParsec s m Char => m Integer

做你想做的事。

vanilla parsec库似乎缺少一些明显的解析器,这导致了“包含电池”parsec衍生包的兴起。我想parsec维护者最终会找到好东西。

https://hackage.haskell.org/package/megaparsec-4.2.0/docs/Text-Megaparsec-Lexer.html

更新

或与vanilla parsec:

Prelude Text.Parsec Text.Parsec.Language Text.Parsec.Token> parse ( integer . makeTokenParser $ haskellStyle ) "integer" "-1234"
Right (-1234)