穷人并发,了解单体实例?



我正在浏览本指南以实现并发,但我无法理解这个monad实例:

data Action m = Atom (m (Action m)) | Fork (Action m) (Action m) | Stop
newtype C m a = C {apply :: (a -> Action m) -> Action m}
instance Monad (C m ) where
m >>= f         = C $ k -> apply m(a -> apply (f a) k) --?
return x        = C $ k -> k x

我了解延续 monads 的基本用途,但我正在努力破译这个声明中发生了什么?

看到C m a实际上只是以延续传递样式(a -> Action m) -> Action m表示的a的计算(在抽象Action m类型上)可能会有所帮助,因此数据构造函数C和相应的记录选择器apply只是语法绒毛。 在没有它们和显式 monad 实例的情况下重写,您将获得以下等效代码。 (请注意,这里Cnt的类型只是一个延续,而不是延续 monad。Control.Monad.Trans.Cont中的延续 monad 更像是我的CPS类型。

type Cnt m a = (a -> Action m)   -- a continuation, not a Cont monad
type CPS m a = Cnt m a -> Action m
bind :: CPS m a -> (a -> CPS m b) -> CPS m b
cps_a `bind` a_to_cps_b = cont_b -> cps_a (a -> a_to_cps_b a cont_b)

或者稍微啰嗦一点:

cps_a `bind` a_to_cps_b =
cont_b -> cps_a (a -> let cps_b = a_to_cps_b a in cps_b cont_b)

这是如何工作的? 好吧,括号中的部分有一个自由变量cont_b这是一个b延续;但是,鉴于这种延续,它只是一个a延续,它使用a_to_cps_b来构造一个应用于自由cont_b延续的b计算(CPS 风格)。 简而言之,括号中的部分是提供的a_to_cps_b,包裹在a延续中。 为了将其与cps_a相结合,我们只需将cps_a应用于此a-延续,这表示由cps_a表示的a-计算与产生b-计算的映射a_to_cps_b的组合,所有这些都在CPS中表示,带有自由变量b_cont。 对这个自由变量进行抽象为我们提供了所需的CPS m b

我认为这可能有助于使整个事情更容易理解,您现在可以回到原始定义,认识到a -> apply (f a) k实际上是f :: a -> C m ba延续版本,以表示b延续的自由变量k表示。apply m可用于将a计算m应用于此a延续,我们留下了一些东西,它将a计算m与从ab计算的映射f相结合,所有这些都以名为k的自由b延续表示, 我们可以 lambda 抽象来构建绑定运算符应该返回的所需b计算。

最新更新