use*_*134 6 monads haskell monad-transformers lifting
我正在学习monad变形金刚,我很困惑在使用电梯时是必要的.假设我有以下代码(它没有做任何有趣的事情,只是我可以带来的最简单的演示).
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
Run Code Online (Sandbox Code Playgroud)
所以有一个monad堆栈,其中主要的块有类型ContT Int (StateT Int Identity) Int.
现在,在第三个when带递归的块中,程序需要升级才能编译.在第二个区块中,不需要升力,但我不知何故假设它是因为它的存在以exit某种方式迫使线上方的线被提升到ContT.但在第一个区块,不需要升力.(但如果明确添加,也没有问题.)这对我来说真的很困惑.我觉得所有的when阻挡都是等效的,无论是在任何地方还是在任何地方都需要升降机.但这显然不是真的.需要/不需要升降机的关键区别在哪里?
Dan*_*zer 11
这里的混乱是因为你正在使用的monad变压器库有点聪明.具体的类型,get并put没有明确提到State或StateT.相反,它们是一致的
get :: MonadState s m => m s
put :: MonadState s m => s -> m ()
Run Code Online (Sandbox Code Playgroud)
因此,只要我们在具有MonadState实现monad 的上下文中使用它,就不需要显式lifts.在您使用get/ putsince的所有实例中都是这种情况
instance MonadState s (StateT s m)
instance MonadState s m => ContT k m
Run Code Online (Sandbox Code Playgroud)
都坚持.换句话说,类型类解析将自动处理为您做适当的提升.这又意味着,你可以在的Elid lift上的S get/ put在你的程序结束.
递归调用不会发生这种情况,因为它的类型是显式的State Int Int.如果你把它推广到MonadState Int m => m Int你甚至可以忽略这个最后的提升.
我想提供一个替代的答案,既肤浅又重要的一切.
你需要使用lift什么时候lift做东西类型检查,否则不要.
是的,这听起来很肤浅,似乎没有任何深刻的含义.但那不是真的.MonadTrans是一类可以将monadic动作以中性方式提升到更大背景的事物.如果你想要技术描述,那么阶级法则提供关于"中立"意味着什么的更明确的规则.但结果是,lift除了使提供的动作与另一种类型兼容之外,什么都不做.
那么 - 做lift什么?它提供了将monadic动作提升为更大类型所需的逻辑.你什么时候需要使用它?当你有一个monadic动作,你需要提升到一个更大的类型.你什么时候有一个monadic动作,你需要提升到更大的类型?这就是类型告诉你的.
这是使用Haskell的关键部分.您可以模块化您对代码的理解.类型系统为您记录大量的簿记.依靠它来使记账正确,所以你只需要保持头脑中的逻辑.编译器和类型系统可作为智能放大器使用.他们照顾得越多,在编写软件时就越不需要保持头脑.
| 归档时间: |
|
| 查看次数: |
271 次 |
| 最近记录: |