首页 文章

在 Logger Monad中使用绑定操作输出的顺序错误

提问于
浏览
0

我正在做一些练习,试图更好地理解Monads及其在Haskell中的绑定操作 . 为此,我决定编写一个 Logger 来跟踪正在执行的操作 . 因此,我创建了以下数据类型:

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 ""

为了在场景中测试 Logger ,我编写了一个表示简单算术运算的数据类型 . 数据类型如下:

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"

1 回答

  • 2

    实际上是按顺序打印日志消息,你可以看到

    evalTrace (Add x y) = do
      rx <- evalTrace x
      ry <- evalTrace y
      Result () "Add\n" >> return (rx + ry)
    

    desugars into evalTrace x >>= (\rx -> evalTrace y >>= (\ry -> Result () "Add\n" >> return (rx + ry))) . 你可以在这里看到 evalTrace x 的日志首先出现,然后是 evalTrace y 的日志,然后是实际的"Add"日志 . 你're doing a post-order Tree traversal of your expressions, as is the case with standard arithmetic interpreters. You'正在寻找:

    do
      log "Add\n"
      rx <- evalTrace x
      ry <- evalTrace y
      return (rx + ry)
    

    log = Result ()

    请注意 do {a;b} desugars到 a >> b ,所以你可以这样编写原始代码:

    evalTrace (Add x y) = do
      rx <- evalTrace x
      ry <- evalTrace y
      Result () "Add\n"
      return (rx + ry)
    

相关问题