一般主题:虽然我发现将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 )
但我无法让它发挥作用。具体来说,我尝试运行堆栈,就像最外层的错误一样:
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的运行顺序(除了{{1}引入的结构中的任何刚性之外) })?
答案 0 :(得分:6)
如果您尝试使用显式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
准确了解您想要的图层,但这会导致更少的麻烦。