我有以下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
答案 0 :(得分:10)
您的问题是<$>
和<*>
等是关联的。这意味着您的行:
binding = Binding <$> word <* (spaces *> string "is" *> spaces) *> expr
将被解释为
binding = (Binding <$> word <* (spaces *> string "is" *> spaces)) *> expr
这意味着它将解析然后忽略最后一个expr之前的所有内容。正如@icktoofay所说,您可以将预期版本编写为:
binding = Binding <$> word <* spaces <* string "is" <* spaces <*> expr
并且根本不需要任何括号,因为左边的关联性。
答案 1 :(得分:6)
@danem是对的,试试:
binding :: Parser Expr
binding = Binding <$> word <*> (spaces *> string "is" *> spaces *> expr)
您的原始定义以这种方式解析:
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)
所以我们看到所有类型的检查都是由于currying以及我们放弃了something
的结果这一事实。