2

我正在使用以下数据类型实现一个非常简单的穷人并发结构:

data C m a = Atomic (m (C m a)) | Done a

我为此创建了一个 monad 实例:

instance Monad m => Monad (C m) where
  (>>=) (Atomic m) f      = Atomic $ (liftM (>>= f) m)
  (>>=) (Done v) f        = f v
  return                  = Done

Q1。我是否正确地说Atomic $ (liftM (>>= f) m)正在创建一个新的Atomicmonad,其中包含f( * -> *) 的结果应用于 内部的值m

Q2。我是否说在Monad m这里使用超类来启用liftM? 如果是这样,因为这是Monad类的一个实例,为什么不能liftM直接访问?

4

1 回答 1

4

Q1。它正在创造Atomic 价值。Monad 是类型级别的映射。因为我们知道它的类型的f第二个参数>>=C m a

f :: Monad m => a -> C m b

因此

(>>= f) :: Monad m => C m a -> C m b

f扩展以展开它的论点,并且

liftM (>>= f) :: (Monad m1, Monad m) => m1 (C m a) -> m1 (C m b)

简单地将m1您的设置中与m. 当您从中提取值m并将Atomic其传递给liftM您时,请使用 monadm的绑定(通过liftM)提取C m a要传递给的内部f。定义的第二部分liftM将结果重新包装为m (C m b)您包装的Atomic. 所以是的。

Q2。是的。但它是liftM底层 monad 的一个mliftMforC m a是根据实例(其和>>=)定义的return。通过使用C m a's liftM(如果您设法根据 进行定义>>=),liftM您将得到一个循环定义。您需要约束Monad m来创建管道m (C m a)以穿过>>=f。事实上,正如 Benjamin Hodgson 所指出的,Monad m这是一个不必要的强约束,Functor m就足够了。事实上,与相关实现一起的C事实在这个问题上给出了最深刻的见解。Free

于 2017-03-16T10:24:57.177 回答