快速定义
您需要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
对问题的另一种看法
查看liftfrom transformerspackage 的定义应该可以帮助您找到答案,因为您想要类似的东西
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是IOmonad 并且t是具有某种状态的状态转换器,并且您的one操作实际上发射了一枚导弹,并根据其成功或失败来修改State. 现在你想要的是在不发射导弹的情况下实际修改状态。一般来说,这是不可能的。
如果您知道一些关于co首先运行哪个操作的信息,那么您可以co'使用上述方法实现。