常规主题:虽然我发现将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
引入的结构上的严格性之外)? 最佳答案
如果您尝试使用显式的monad转换器堆栈键入计算,则会得到更多信息性的类型错误:
mStack :: ErrorT String (StateT (Map String Int) (StateT Int Writer)) Int
如果这样做,
ghc
会更早捕获类型错误。原因是在最顶层的mStack
中使用了以下两个命令:modify (+1) -- i.e. from `tick`
...
yourMap <- lift get
如果要给它一个明确的堆栈,那么您会发现错误:
modify
和lift get
都将以它们遇到的第一个StateT
层为目标,而这恰好是同一个StateT
层。modify
从ErrorT
层开始,一直向下直到到达外部StateT
层为止,并得出结论,外部StateT
必须使用Int
状态。 get
从外部StateT
层开始,注意它已经在StateT
层中,并且完全忽略了内部StateT
层,因此得出结论,外部StateT
层必须存储Map
。ghc
然后说:“有什么用?该层不能同时存储Int
和Map
!”,这说明了您遇到的类型错误。但是,因为您使用类型类而不是具体的monad转换器堆栈,所以ghc
在等待直到指定具体堆栈之前就无法知道这是类型错误。解决方案很简单:只需在您的
lift
中添加另一个get
,它便会按您的预期定位内部StateT
层。我个人更喜欢完全避免
mtl
类,并且始终单独使用transformers
库与具体的monad转换器堆栈一起工作。它比较冗长,因为您必须精确地确定要使用lift
的哪一层,但这会减少以后的麻烦。