我正在学习单子(monad)更改器(mutator),我对何时需要使用电梯感到困惑。
假设我有以下代码(它没有做任何有趣的事情,只是我可以用来演示的最简单的代码)。
foo :: Int -> State Int Int
foo x = do
(`runContT` pure) $ do
callCC $ \exit -> do
when (odd x) $ do
-- lift unnecessary
a <- get
put $ 2*a
when (x >= 5) $ do
-- lift unnecessary, but there is exit
a <- get
exit a
when (x < 0) $ do
-- lift necessary
a <- lift $ foo (x + 10)
lift $ put a
lift get
所以有一个 monad 堆栈,其中 main do block 的类型为
ContT Int (StateT Int Identity) Int
.现在,在第三个
when
do block with recursion 编译程序需要一个提升。在第二个街区,不需要电梯,但我不知何故认为这是因为 exit
的存在。以某种方式强制将线上方的线提升到 ContT
.但在第一个街区,不需要电梯。 (但如果明确添加,也没有问题。)这真的让我很困惑。我觉得所有的when
do block 是等效的,无论在任何地方都应该需要电梯。但这显然不是真的。需要/不需要电梯的关键区别在哪里?
最佳答案
由于您使用的 monad 转换器库有点聪明,因此这里出现了困惑。具体来说,get
的类型和 put
没有明确提及 State
或 StateT
.相反,它们是沿线的
get :: MonadState s m => m s
put :: MonadState s m => s -> m ()
因此,只要我们在带有
MonadState
的上下文中使用它实现 monad 不需要显式 lift
s。在您使用 get
的所有实例中都是这种情况。/put
自从instance MonadState s (StateT s m)
instance MonadState s m => ContT k m
两者都持有。换句话说,类型类解析将自动为您处理适当的提升。这反过来意味着您可以省略
lift
上 get
/put
在你的程序结束时。递归调用不会发生这种情况,因为它的类型是明确的
State Int Int
.如果将其概括为 MonadState Int m => m Int
你甚至可以省略这最后的电梯。
关于haskell - monad 变压器何时需要提升?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45236226/