在Haskell中如何实现一个已经完成实际解析的Read实例?

时间:2016-08-07 15:59:57

标签: haskell

this answer中,人们发现声明为数据类型Read实现Tree实例并不是很难,而实际的解析已经完成。

但是,我很难理解read这样的功能如何运作:AFAIK,我应该实现readsPrec函数而不是readreadsPrec应该只对一个字符组成的字符串执行读取操作。这是对的吗? 如果是这种情况,那么当通过Read进行解析时,如何实现Tree ParsecT read实例?我们可以逐字逐句解析,还是有必要这样做?

我没想到Hoogle是Haskell中的一个难题,但现在我觉得它很困惑和混乱,我迷失了readP_to_S所有这些不熟悉的东西readS 1}},getElem

非常感谢任何帮助或参考。

1 个答案:

答案 0 :(得分:6)

我一直想知道这件事,你的问题促使我调查它。

摘要:最简单的手动方式:

instance Read Tree where
    readsPrec _ str = [(parsecRead str,"")]

但是deriving是更安全的选择;以上内容不适用于[Tree]和其他数据类型。我的理解是ShowRead 无意手动实施;他们应该派生并使用语法正确的Haskell表达式

看起来Read并不像

那么简单
class Read a where
    read :: String -> a

是有一个解析器组合系统,类似但不同于Parsec,它被设计成模块化,递归等等。但由于我们已经在使用不同的解析器组合库Parsec,我认为最好尽可能少地混淆其他系统。

Prelude文档说明Read的最小完整实现是readsPrecreadPrec。后者被描述为“使用新式解析器建议替换readsPrec(仅限GHC)”。这对我来说有点麻烦,所以让我们继续实施readsPrec

类型是

readsPrec :: Read a => Int -> ReadS a
type ReadS a = String -> [(a,String)]

并且ReadS的文档读取“类型为a的解析器”,表示为带String的函数,并返回可能的解析列表(a,String)对”。对我而言,“解析”是什么并不完全明显,但是对source code for read in Text.Read的一看是显而易见的:

read :: Read a => String -> a
read s = either errorWithoutStackTrace id (readEither s)

readEither :: Read a => String -> Either String a
readEither s =
    -- minPrec is defined as 0 in Text.ParserCombinators.ReadPrec
    case [ x | (x,"") <- readPrec_to_S read' minPrec s ] of
      [x] -> Right x
      []  -> Left "Prelude.read: no parse"
      _   -> Left "Prelude.read: ambiguous parse"
  where
   read' = -- read' :: P.ReadPrec a
     do x <- readPrec
        lift P.skipSpaces -- P is Text.ParserCombinators.ReadP
        return x

我试图扩展readPrec_to_S等的定义,但我觉得这不值得。我认为该定义明确指出我们应该将[(x,"")]作为成功的解析返回。

readsPrec的整数参数似乎是“优先级上下文”。我的猜测是,如果我们只想一次解析一棵树就可以安全地忽略它,但如果我们试图解析[Tree]的实例,那么忽略它会导致问题。我会忽略它,因为我不认为这是值得的。

简而言之,如果我们在您引用的帖子中定义了parsecRead :: String -> Tree(作者称之为read'

instance Read Tree where
    readsPrec _ str = [(parsecRead str,"")]

如果我们检查一下它在程序中是如何工作的(使用原始提问者提供的Show实例):

main = do
    print (read "ABC(DE)F" == example)
    print ([read "ABC(DE)F", read "ABC(DE)F"] :: [Tree])
    print (read "[ABC(DE)F,ABC(DE)F]" :: [Tree])

我们得到了

True
[ABC(DE)F,ABC(DE)F]
Test.hs: Prelude.read: no parse

这里的复杂性和缺乏文档实际上让我认为deriving (Read)实际上是唯一安全的选择,除非您愿意深入了解优先级的细节。 我想我已经读过ShowRead实际上主要是为了派生的某个地方,并且这些字符串旨在语法正确的Haskell表达式< / strong>(如果我错了,请纠正我)。对于更一般的解析,像Parsec这样的库可能是更好的选择。

如果您有能力亲自查看源代码,相关模块似乎是