如何编码采用免费(或更自由)monad的monadic参数的动作?

Pet*_*lák 9 monads haskell free-monad

大多数monadic函数采用纯参数并返回monadic值.但也有一些需要monadic论证,例如:

mplus :: (MonadPlus m) => m a -> m a -> m a

finally :: IO a -> IO b -> IO a

forkIO :: m () -> m ThreadId

-- | From Control.Monad.Parallel
forkExec :: m a -> m (m a)
Run Code Online (Sandbox Code Playgroud)

他们每个人似乎都提出了一个不同的问题,我无法掌握如何使用免费monad编码此类操作的通用方法.

use*_*465 3

我只会回答Freermonad 部分。回想一下定义:

data Freer f b where
    Pure :: b -> Freer f b
    Roll :: f a -> (a -> Freer f b) -> Freer f b
Run Code Online (Sandbox Code Playgroud)

现在与

data NonDetEff a where
  MZero :: NonDetEff a
  MPlus :: NonDetEff Bool
Run Code Online (Sandbox Code Playgroud)

我们可以定义

type NonDetComp = Freer NonDetEff
Run Code Online (Sandbox Code Playgroud)

Roll应用于 ,时MPlusa与 统一Bool,第二个参数的类型是Bool -> NonDetEff bwhich 基本上是一个元组:

tuplify :: (Bool -> a) -> (a, a)
tuplify f = (f True, f False)

untuplify :: (a, a) -> (Bool -> a)
untuplify (x, y) True  = x
untuplify (x, y) False = y
Run Code Online (Sandbox Code Playgroud)

举个例子:

ex :: NonDetComp Int
ex = Roll MPlus $ Pure . untuplify (1, 2)
Run Code Online (Sandbox Code Playgroud)

所以我们可以定义一个MonadPlus用于非确定性计算的实例

instance MonadPlus NonDetComp where
    mzero       = Roll MZero Pure
    a `mplus` b = Roll MPlus $ untuplify (a, b)
Run Code Online (Sandbox Code Playgroud)

并运行它们

run :: NonDetComp a -> [a]
run (Pure x)       = [x]
run (Roll MZero f) = []
run (Roll MPlus f) = let (a, b) = tuplify f in run a ++ run b
Run Code Online (Sandbox Code Playgroud)