我正在学习单子变换器,但是对于何时使用lift感到困惑。 假设我有以下代码(它并没有做什么有趣的事情,只是为了演示而已)。
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
所以有一个单子堆栈,其中主要的 do 块的类型为
ContT Int (StateT Int Identity) Int
。现在,在第三个带有递归的
when
do 块中需要使用 lift 才能编译程序。在第二个块中,不需要 lift,但我认为这是因为存在 exit
,它会强制将上一行提升到 ContT
。但在第一个块中,不需要 lift。(但如果明确添加,也没有问题。)这真的让我很困惑。我觉得所有的 when
do 块都是等价的,要么必须在所有地方都需要 lift,要么就不需要。但显然这不是真的。是什么关键差异使得 lift 必须/不必要?