记录器 Monad 中绑定操作的输出顺序错误
Wrong order of output with bind operations in logger Monad
我正在做一些练习,试图更好地理解 Monad 及其在 Haskell 中的绑定操作。为此,我决定编写一个记录器来跟踪执行的操作。因此,我创建了以下数据类型:
data Log e a = Error e | Result a String deriving (Show)
作为 Monad 实例:
instance Monad (Log e) where
(>>=) (Error e) _ = Error e
(>>=) (Result x log) f = case f x of
Error e -> Error e
Result r log' -> Result r (log ++ log')
return x = Result x ""
为了在场景中测试记录器,我写了一个表示简单算术运算的数据类型。数据类型如下:
data Exp = Lit Int | Add Exp Exp | Mul Exp Exp | Div Exp Exp deriving Show
存在除法,我想使用 Log 数据类型的 Error 构造函数来处理除零。
评估表达式并在途中跟踪的 eval 函数如下所示:
evalTrace :: Exp -> Log String Int
查询最终结果应该是什么样子:
Main> evalTrace (Add (Lit 1) (Mul (Lit 2) (Lit 3)))
(7, "Add\nLit\nMul\nLit\nLit\n")
这是我到目前为止写的:
evalTrace :: Exp -> Log String Int
evalTrace (Lit x) = Result () "Lit\n" >> return x
evalTrace (Add x y) = do
rx <- evalTrace x
ry <- evalTrace y
Result () "Add\n" >> return (rx + ry)
evalTrace (Mul x y) = do
rx <- evalTrace x
ry <- evalTrace y
Result () "Mul\n" >> return (rx * ry)
evalTrace (Div x y) = do
rx <- evalTrace x
ry <- evalTrace y
if ry == 0
then (Error "division by zero")
else Result () "Div\n" >> return (div rx ry)
算法处理正确,但日志消息打印顺序似乎不正确。我确定我在这里遗漏了一些明显的东西,但我似乎无法解决问题。
查询:
Main> evalTrace (Add (Lit 1) (Mul (Lit 2) (Lit 3)))
Result 7 "Lit\nLit\nLit\nMul\nAdd\n"
日志信息其实是按顺序打印的,可以看到
evalTrace (Add x y) = do
rx <- evalTrace x
ry <- evalTrace y
Result () "Add\n" >> return (rx + ry)
脱糖为 evalTrace x >>= (\rx -> evalTrace y >>= (\ry -> Result () "Add\n" >> return (rx + ry)))
。
你可以在这里明显看到 evalTrace x
的日志应该在第一位,然后是 evalTrace y
的日志,然后是实际的 "Add" 日志。
您正在对表达式进行 post-order 树遍历,就像标准算术解释器的情况一样。
您正在寻找:
do
log "Add\n"
rx <- evalTrace x
ry <- evalTrace y
return (rx + ry)
和log = Result ()
请注意 do {a;b}
脱糖为 a >> b
,因此您可以这样编写原始代码:
evalTrace (Add x y) = do
rx <- evalTrace x
ry <- evalTrace y
Result () "Add\n"
return (rx + ry)
我正在做一些练习,试图更好地理解 Monad 及其在 Haskell 中的绑定操作。为此,我决定编写一个记录器来跟踪执行的操作。因此,我创建了以下数据类型:
data Log e a = Error e | Result a String deriving (Show)
作为 Monad 实例:
instance Monad (Log e) where
(>>=) (Error e) _ = Error e
(>>=) (Result x log) f = case f x of
Error e -> Error e
Result r log' -> Result r (log ++ log')
return x = Result x ""
为了在场景中测试记录器,我写了一个表示简单算术运算的数据类型。数据类型如下:
data Exp = Lit Int | Add Exp Exp | Mul Exp Exp | Div Exp Exp deriving Show
存在除法,我想使用 Log 数据类型的 Error 构造函数来处理除零。
评估表达式并在途中跟踪的 eval 函数如下所示:
evalTrace :: Exp -> Log String Int
查询最终结果应该是什么样子:
Main> evalTrace (Add (Lit 1) (Mul (Lit 2) (Lit 3)))
(7, "Add\nLit\nMul\nLit\nLit\n")
这是我到目前为止写的:
evalTrace :: Exp -> Log String Int
evalTrace (Lit x) = Result () "Lit\n" >> return x
evalTrace (Add x y) = do
rx <- evalTrace x
ry <- evalTrace y
Result () "Add\n" >> return (rx + ry)
evalTrace (Mul x y) = do
rx <- evalTrace x
ry <- evalTrace y
Result () "Mul\n" >> return (rx * ry)
evalTrace (Div x y) = do
rx <- evalTrace x
ry <- evalTrace y
if ry == 0
then (Error "division by zero")
else Result () "Div\n" >> return (div rx ry)
算法处理正确,但日志消息打印顺序似乎不正确。我确定我在这里遗漏了一些明显的东西,但我似乎无法解决问题。
查询:
Main> evalTrace (Add (Lit 1) (Mul (Lit 2) (Lit 3)))
Result 7 "Lit\nLit\nLit\nMul\nAdd\n"
日志信息其实是按顺序打印的,可以看到
evalTrace (Add x y) = do
rx <- evalTrace x
ry <- evalTrace y
Result () "Add\n" >> return (rx + ry)
脱糖为 evalTrace x >>= (\rx -> evalTrace y >>= (\ry -> Result () "Add\n" >> return (rx + ry)))
。
你可以在这里明显看到 evalTrace x
的日志应该在第一位,然后是 evalTrace y
的日志,然后是实际的 "Add" 日志。
您正在对表达式进行 post-order 树遍历,就像标准算术解释器的情况一样。
您正在寻找:
do
log "Add\n"
rx <- evalTrace x
ry <- evalTrace y
return (rx + ry)
和log = Result ()
请注意 do {a;b}
脱糖为 a >> b
,因此您可以这样编写原始代码:
evalTrace (Add x y) = do
rx <- evalTrace x
ry <- evalTrace y
Result () "Add\n"
return (rx + ry)