在this answer中,人们发现声明为数据类型Read
实现Tree
实例并不是很难,而实际的解析已经完成。
但是,我很难理解read
这样的功能如何运作:AFAIK,我应该实现readsPrec
函数而不是read
和readsPrec
应该只对一个字符组成的字符串执行读取操作。这是对的吗?
如果是这种情况,那么当通过Read
进行解析时,如何实现Tree
ParsecT
read
实例?我们可以逐字逐句解析,还是有必要这样做?
我没想到Hoogle
是Haskell中的一个难题,但现在我觉得它很困惑和混乱,我迷失了readP_to_S
所有这些不熟悉的东西readS
1}},getElem
等
非常感谢任何帮助或参考。
答案 0 :(得分:6)
我一直想知道这件事,你的问题促使我调查它。
摘要:最简单的手动方式:
instance Read Tree where
readsPrec _ str = [(parsecRead str,"")]
但是deriving
是更安全的选择;以上内容不适用于[Tree]
和其他数据类型。我的理解是Show
和Read
无意手动实施;他们应该派生并使用语法正确的Haskell表达式。
看起来Read
并不像
class Read a where
read :: String -> a
是有一个解析器组合系统,类似但不同于Parsec,它被设计成模块化,递归等等。但由于我们已经在使用不同的解析器组合库Parsec,我认为最好尽可能少地混淆其他系统。
Prelude文档说明Read
的最小完整实现是readsPrec
或readPrec
。后者被描述为“使用新式解析器建议替换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)
实际上是唯一安全的选择,除非您愿意深入了解优先级的细节。
我想我已经读过Show
和Read
实际上主要是为了派生的某个地方,并且这些字符串旨在语法正确的Haskell表达式< / strong>(如果我错了,请纠正我)。对于更一般的解析,像Parsec
这样的库可能是更好的选择。
如果您有能力亲自查看源代码,相关模块似乎是