我在Graham Hutton的Haskell编程的第8章中,我正在复制代码并在GHC中对其进行测试。
请参阅此处的幻灯片:http://www.cis.syr.edu/~sueo/cis352/chapter8.pdf,特别是幻灯片15
到目前为止我复制的相关代码是:
type Parser a = String -> [(a, String)]
pih_return :: a -> Parser a
pih_return v = \inp -> [(v, inp)]
failure :: Parser a
failure = \inp -> []
item :: Parser Char
item = \inp -> case inp of
[] -> []
(x:xs) -> [(x,xs)]
parse :: Parser a -> String -> [(a, String)]
parse p inp = p inp
sat :: (Char -> Bool) -> Parser Char
sat p = do x <- item
if p x then pih_return x else failure
我已将书中的return
功能名称更改为pih_return
,以便它不会与Prelude return
功能发生冲突。
错误位于最后一个函数sat
中。我直接从书中复制了这个。
您可能会看到p
是Char
到Bool
的函数(例如isDigit
),而x
的类型为[(Char, String)]
,这是第一个错误。
然后pih_return
获取值v
并返回[(v, inp)]
,其中inp
是String
。这会导致sat
出错,因为传递的v
为x
,而不是Char
。
通过明确将inp
加入sat
sat :: (Char -> Bool) -> Parser Char
sat p inp = do x <- item inp
if p (fst x) then pih_return (fst x) inp else failure inp
这是解决问题的最佳方法吗?
答案 0 :(得分:5)
第一个sat
无效,Parser
必须是monad才能使用do
表示法。为了使它成为monad实例,必须使用newtype
。
我不拥有这本书,但我想作者想要从一个简单的解析器类型开始,然后将它扩展为一个完整的monad,我怀疑你已经将非monadic版本的定义混淆了Parser
monad(sat
)并错过了monad实例声明。
可用章节中的代码on the author's web site,其中为Parser
定义了monad实例。
如果你必须为简单的sat
类型写一个Parser
函数,我宁愿用lambda风格(如item
)来做,并完全避免monad(你注意到了原始sat
的{{1}}块是do
monad,你的是Parser
monad?)。我认为您的List
版本中存在错误:而不是sat
,我认为它应该是pih_return (fst x) inp
。
答案 1 :(得分:2)
如果没有monad,则无法使用do
表示法,除非使用data
或newtype
,否则无法创建monad实例,并且无法使用{{ 1}}或data
除非你引入恼人的值构造函数。毫无疑问,值构造函数已被省略,因为它 令人讨厌。
在下面的示例中,您可以看到我使用了newtype
并引入了恼人的值构造函数newtype
。这是使Parser
声明有效的原因,此时您不仅可以使用instance
- 符号,还可以使用标准monadic do
和return
。
此代码编译时没有错误或警告:
fail
答案 2 :(得分:1)
幻灯片缺少类型Monad
的{{1}}类型类的实现。
使用该声明,Parser
表示法是正确的; do
实际上正确地从x <- item
展开到[(Char, String)]
。
我似乎没有得到这个定义而没有编译它以查看错误但它是一个开始:
(Char, String)
答案 3 :(得分:1)
我正在阅读同一本书,并在尝试做练习时遇到了同样的问题。我从使用'do ...'符号恢复到&gt;&gt; =。对于任何有兴趣的人,我将我的代码包含在使用nat函数中。我将所有函数添加到a并更改了&gt;&gt; = to&gt;&gt;&gt; =以避免名称与prelude碰撞。
type AParser a = String -> [(a, String)]
areturn :: a -> AParser a
areturn v = \inp -> [(v, inp)]
afailure :: AParser a
afailure = \inp -> []
aitem :: AParser Char
aitem = \inp -> case inp of
[] -> []
(x:xs) -> [(x, xs)]
aparse :: AParser a -> String -> [(a, String)]
aparse p inp = p inp
(>>>=) :: AParser a -> (a -> AParser b) -> AParser b
p >>>= f = \inp -> case aparse p inp of
[] -> []
[(v, out)] -> aparse (f v) out
(+++) :: AParser a -> AParser a -> AParser a
p +++ q = \inp -> case aparse p inp of
[] -> aparse q inp
[(v, out)] -> [(v, out)]
asat :: (Char -> Bool) -> AParser Char
asat p = aitem >>>= (\x -> if p x then areturn x else afailure)
adigit :: AParser Char
adigit = asat isDigit
amany :: AParser a -> AParser [a]
amany p = amany1 p +++ areturn []
amany1 :: AParser a -> AParser [a]
amany1 p = p >>>= (\v -> (amany p) >>>= (\vs -> areturn (v:vs)))
anat :: AParser Int
anat = amany1 adigit >>>= (\xs -> areturn (read xs))