通常,当 monadic 操作是 pure
/return
调用的结果时,有一种安全的方法可以从 monadic 操作中extract
。 例如 unsafePerformIO (pure 42)
不会影响世界。只要我只使用这些一元运算,我在纯碎片中是安全的。
我对推广这一概念的纪律性方法感兴趣。我的第一个想法是使用Either
和注射newtype
Left
用于纯值,并在无法证明纯度时Right
。编写 monad 实例相当简单。我甚至可以把它做成一个单体变压器。我也知道免费的monad(变压器)结构。
专家对我应该走哪条路有什么看法?
对于Applicative
,transformers
包中有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