快速定义
您需要t m
有一个 monad 实例才能使其工作。
import Control.Monad
import Control.Monad.Trans
co :: Monad m => m a -> m a -> m a
co = undefined
co' :: (MonadTrans t, Monad m, Monad (t m)) => t m a -> m a -> t m a
co' one two = lift . flip co two . return =<< one
对问题的另一种看法
查看lift
from transformers
package 的定义应该可以帮助您找到答案,因为您想要类似的东西
lift . return = return
lift (m >>= f) = lift m >>= (lift . f)
正如你所拥有的flip co one :: Monad m => m a -> m a
,并且你想提升它以获得 type 的功能(MonadTrans t, Monad m, Monad (t m)) => t m a -> t m a
。所以跟随电梯的脚步
lift' :: (MonadTrans t, Monad m, Monad (t m)) => (m a -> m a) -> t m a -> t m a
lift' f tma = tma >>= (lift . f . return)
现在定义co'
是微不足道的
co' one two = lift' (flip co two) one
问题
上面的解决方案只是满足类型,但它们也满足语义吗?要查看问题,让我们采用一个co
始终返回第二个动作而不看第一个动作的函数。现在上面co'
永远无法做到这一点,因为它总是在决定任何事情之前运行第一个动作。所以第一个动作的副作用仍然会发生在co'
即使它们没有发生在co
。
是否存在通用解决方案?
我想不会。因为您想要实际实现该功能的一般功能是一个类似 的功能t m a -> (m a -> m b) -> t m b
,您需要在其中执行操作m a
而不t m a
实际运行m a
.
因此,假设m
是IO
monad 并且t
是具有某种状态的状态转换器,并且您的one
操作实际上发射了一枚导弹,并根据其成功或失败来修改State
. 现在你想要的是在不发射导弹的情况下实际修改状态。一般来说,这是不可能的。
如果您知道一些关于co
首先运行哪个操作的信息,那么您可以co'
使用上述方法实现。