9

大多数 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)

他们每个人似乎都提出了一个不同的问题,我无法掌握如何使用自由单子对这些动作进行编码的通用方法。

  • 在两者中finallyforkIO问题在于一元参数的类型与结果不同。但是免费的人需要它们是相同的类型,因为IO a被编码类型的类型变量替换,比如data MyFunctor x = Finally x x x,它只会 encode IO a -> IO a -> IO a

    33 行 Haskell 代码中从零到协作线程作者Fork next next用来拳头实现

    cFork :: (Monad m) => Thread m Bool
    cFork = liftF (Fork False True)
    

    然后用它来实现

    fork :: (Monad m) => Thread m a -> Thread m ()
    

    其中输入和输出具有不同的类型。但我不明白这是使用某些过程得出的,还是只是一个适用于这个特定目的的临时想法。

  • mplus特别令人困惑:天真的编码为

    data F b = MZero | MPlus b b
    

    分布>>=建议更好的实现更复杂。并且从 free 中删除了 free 的本MonadPlus 实现

    更自由的情况下,它是通过添加来实现的

    data NonDetEff a where
      MZero :: NonDetEff a
      MPlus :: NonDetEff Bool
    

    为什么是MPlus NonDetEff Bool而不是NonDetEff a a除了使用CoYoneda 函子之外,有没有办法让它与Free我们需要数据类型成为函子一起工作?

  • 因为forkExec我根本不知道如何进行。
4

1 回答 1

3

我只会回答Freer单子部分。回想一下定义:

data Freer f b where
    Pure :: b -> Freer f b
    Roll :: f a -> (a -> Freer f b) -> Freer f b

现在有了

data NonDetEff a where
  MZero :: NonDetEff a
  MPlus :: NonDetEff Bool

我们可以定义

type NonDetComp = Freer NonDetEff

Roll应用于 , 时MPlus,与第二个参数的类型a是统一的,它基本上是一个元组:BoolBool -> NonDetEff b

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

举个例子:

ex :: NonDetComp Int
ex = Roll MPlus $ Pure . untuplify (1, 2)

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

instance MonadPlus NonDetComp where
    mzero       = Roll MZero Pure
    a `mplus` b = Roll MPlus $ untuplify (a, b)

并运行它们

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
于 2015-12-21T10:34:03.690 回答