应用式 Parsec 的问题

Problems with Applicative-style Parsec

我有以下 ADT:

type Program = [Expr]
data Expr =
    Num Int
    | Bool Bool
    | Binding String Expr
    deriving (Show)

这是一个变量绑定表达式的解析器,格式为 lhs is rhs

binding :: Parser Expr
binding = do
    lhs <- word
    spaces
    string "is"
    spaces
    rhs <- expr
    return $ Binding lhs rhs

它工作正常,但是当我尝试将它转换为应用样式时,它给出了错误的结果。

binding :: Parser Expr
binding = Binding <$> word <* (spaces *> string "is" *> spaces) *> expr

在括号部分用>>替换*>也没有用。这两个实现有什么区别?是否有用于组合两个解析器并忽略两者结果的组合器?

尝试使用 Debug.trace 进行调试也没有用...没有打印任何内容。

binding :: Parser Expr
binding = (\x y -> trace (show (x, y)) (Binding x y)) <$> word <* (spaces *> string "is" *> spaces) *> expr

解析器的其余部分,对于上下文:

word :: Parser String
word = many1 letter

expr :: Parser Expr
expr = binding <|> atom

program :: Parser Program
program = do
    spaces
    result <- many (expr <* spaces)
    return result

@danem 说得对,试试:

binding :: Parser Expr
binding = Binding <$> word <*> (spaces *> string "is" *> spaces *> expr)

完整来源:http://lpaste.net/121011

您的原始定义是这样解析的:

binding = ((Binding <$> word) <* (spaces *> string "is" *> spaces)) *> expr

即它具有 something *> expr 的形式,因此返回值仅由最后一个 expr 决定。 lhs 和 is 标记被解析但随后被丢弃。

下面是子表达式类型检查的方式:

Binding                     :: String -> Expr -> Expr
(Binding <$> word)          :: Parser (Expr -> Expr)
(Binding <$> word) <* (...) :: Parser (Expr -> Expr)

所以我们看到由于柯里化而进行的所有类型检查以及我们正在丢弃 something.

的结果这一事实

你的问题是 <$><*> 等是左关联的。这意味着您的行:

binding = Binding <$> word <* (spaces *> string "is" *> spaces) *> expr

将被解释为

binding = (Binding <$> word <* (spaces *> string "is" *> spaces)) *> expr

这意味着它将解析然后忽略最后一个表达式之前的所有内容。正如@icktoofay 所说,您可以将预期的版本写为:

binding = Binding <$> word <* spaces <* string "is" <* spaces <*> expr

而且根本不需要任何括号,因为左结合性。