应用式 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)
您的原始定义是这样解析的:
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
而且根本不需要任何括号,因为左结合性。
我有以下 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)
您的原始定义是这样解析的:
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
而且根本不需要任何括号,因为左结合性。