为什么我在这个解析器序列中遇到类型错误(Erik Meijer的讲座8)?

时间:2015-01-28 19:43:25

标签: parsing haskell types monads

我正在观看Erik Meijer撰写的功能编程基础系列讲座(Graham Hutton的幻灯片)。

lecture 8 (on functional parsers)中,在定义Parser a类型后,引入一些解析原语(包括itemreturn,我将其命名为return'),Erik使用do语法提供simple example如何将解析器组合在一个序列中:

type Parser a = String -> [(a,String)]

item :: Parser Char                                                  
item = \inp -> case inp of                                                   
                 []     -> []                                             
                 (x:xs) -> [(x,xs)]

return' :: a -> Parser a                                                   
return' v = \inp -> [(v,inp)]

p :: Parser (Char,Char)
p = do x <- item
       item
       y <- item
       return' (x,y)

然而,当我尝试在GHCi中加载它时,我得到以下类型错误,我没有预料到并且不理解:

Couldn't match type ‘[(Char, String)]’ with ‘Char’
Expected type: String -> [((Char, Char), String)]
  Actual type: Parser ([(Char, String)], [(Char, String)])
In a stmt of a 'do' block: return' (x, y)
In the expression:
  do { x <- item;
       item;
       y <- item;
       return' (x, y) }
Failed, modules loaded: none.

我做错了什么?

2 个答案:

答案 0 :(得分:5)

此错误消息有点令人困惑,因为GHC将您的Parser类型解压缩到其String -> [(a, String)]的定义中。如果我们撤消这种转变,它将开始变得更有意义:

Expected type: Parser (Char, Char)
  Actual type: Parser ([(Char, String)], [(Char, String)])

现在它开始变得更有意义了。它看起来是你将两次调用的结果带到Parser Char(即[(Char, String)]),然后将它们用作正常的Char s。错误发生在第15行,即您对return'的调用:

return' (x, y)

这告诉我们xy是两个有问题的结果。

你所拥有的do块正在使用monad实例来处理String -> b类型的函数;这样做是通过将输入(String -> b)串起来通过所有函数将它们组合成更大的String函数。像x <- item这样的行允许您从b函数中获取String -> b元素,同时保持所有管道传递String参数。不幸的是,itemParser Char,这意味着b实际上是[(Char, String)],而不仅仅是Char

需要这个额外返回的结构才能正确解析。 String表示未用于解析Char(或您正在解析的任何内容)的其余输入,并且整个事情是一个列表,因为可以有多种可能的方法来有效地解析部分所有需要处理的输入。

要做到这一点,你必须有一些方法来处理这个额外的结构。但是,由于我没有按照课程或观看讲座,我不知道它是什么。您可以定义您自己的 monad实例,而不是依赖于函数的内置实例,但是您可能没有足够深入到自己的类中,或者Meijer可能正在使用非标准库,以避免此问题,在这种情况下,您需要确保您的设置符合类所期望的。

答案 1 :(得分:3)

问题是你的p定义中的记号没有使用你想要它使用的monad。

我可能错了,但看起来p正在使用Reader monad版本。

如果您使用此定义,则会看到xy不是Char值,但类型为[(Char,String)]

import Debug.Trace

p :: Parser (Char,Char)
p = do x <- item
       y <- item
       let foo = trace ("x = " ++ show x) 'a'
           bar = trace ("y = " ++ show y) 'b'
       return' (foo,bar)

test = p "qwe"

如果您想使用do表示法,则应为newtype创建dataParser声明,以便定义绑定运算符(&gt;&gt; =)有效,例如:

newtype Parser a = Parser { parse :: String -> [ (a,String) ] }

instance Monad Parser where
    return   = Parser . return'
    (>>=)    = ...

以下是关于如何使用newtype定义解析器monad实例的好文章:(link)