一般主题:虽然我发现将 monad 堆叠在一起的想法非常吸引人,但我很难想象代码是如何执行的,以及运行层的适当顺序是什么。下面是堆栈的一个示例:Writer、State、State 和 Error,没有特定的顺序(或者是否存在?)。
-----------------------
-- Utility Functions --
-----------------------
type Memory = Map String Int
type Counter = Int
type Log = String
tick :: (MonadState Counter m) => m ()
tick = modify (+1)
record :: (MonadWriter Log m) => Log -> m ()
record msg = tell $ msg ++ "; "
------------------
-- MonadT Stack --
------------------
mStack :: ( MonadTrans t, MonadState Memory m, MonadState Counter (t m), MonadError ErrMsg (t m), MonadWriter Log (t m) ) => t m Int
mStack = do
tick
m <- lift get
let x = fromJust ( M.lookup "x" m ) in x
record "accessed memory"
case True of
True -> return 100
False -> throwError "false"
请注意mStack
,是否抛出错误与函数的任何其他部分无关。
现在理想情况下,我希望输出如下所示:
( Right 100, 1, "accessed memory", fromList [...])
或一般来说:
( output of errorT, output of stateT Counter, output of writerT, output of StateT Memory )
但我无法让它工作。具体来说,我尝试运行堆栈,就好像 Error 在最外层一样:
mem1 = M.fromList [("x",10),("y",5)]
runIdentity $ runWriterT (runStateT (runStateT (runErrorT mStack ) 0 ) mem1 ) ""
但我收到此错误消息:
Couldn't match type `Int' with `Map [Char] Int'
除了上面的例子,一般来说,当我打电话时:
runMonadT_1 ( runMonadT_2 expr param2 ) param1
,
与monadT_2
运行相关的功能是否首先运行,然后将输出通过管道传输到与相关的功能monadT_1
?所以换句话说,就像上面函数中的代码一样mStack
,执行顺序是否完全取决于 monadT 的运行顺序(除了由 引入的结构中的任何刚性lift
)?