显式跟踪副作用 monad 中的纯操作



通常,当 monadic 操作是 pure/return 调用的结果时,有一种安全的方法可以从 monadic 操作中extract。 例如 unsafePerformIO (pure 42)不会影响世界。只要我使用这些一元运算,我在纯碎片中是安全的。

我对推广这一概念的纪律性方法感兴趣。我的第一个想法是使用Either和注射newtype Left用于纯值,并在无法证明纯度时Right。编写 monad 实例相当简单。我甚至可以把它做成一个单体变压器。我也知道免费的monad(变压器)结构。

专家对我应该走哪条路有什么看法?

对于Applicativetransformers包中有Lift

Lift向已经存在的Applicative中添加了一个显式纯计算,并为<*>提供了一个或两个参数显式纯参数的情况提供了"优化"的实现。

当基础Applicative<*>操作在某些方面成本高昂时,这可能很有用。例如,考虑通过分叉线程并行运行两个IO操作的Control.Concurrent.Async的有用Concurrently应用。

执行类似的东西

pure (+) <*> pure 5 <*> Other (Concurrently (return 5)) :: Lift Concurrently Int

不会分叉任何线程。

至于Monad实例,这样的事情可以工作:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype Foo a = Foo { getFoo :: Lift IO a } deriving (Functor, Applicative)      
instance Monad Foo where
    return = pure
    (Foo m) >>= k = case m of
        Pure a -> k a 
        Other m -> Foo (Other (m >>= unLift . getFoo . k))

我想知道为什么Lift还没有这样的实例。

正如@danidiaz所写,对于应用函子,已经有Lift .但是,monad(与应用函子不同)不组成,因此为此有必要创建一个monad转换器:

import Control.Monad
import Control.Monad.Trans
data Impure m a = Pure a | Impure (m a)
instance (Monad m) => Monad (Impure m) where
    return = Pure
    (Pure x) >>= f = f x
    (Impure k) >>= f = Impure $ k >>= x -> case f x of
                                                Pure y -> return y
                                                Impure l -> l
instance MonadTrans Impure where
    lift = Impure

最新更新