我正在浏览本指南以实现并发,但我无法理解这个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 b
的a
延续版本,以表示b
延续的自由变量k
表示。apply m
可用于将a
计算m
应用于此a
延续,我们留下了一些东西,它将a
计算m
与从a
到b
计算的映射f
相结合,所有这些都以名为k
的自由b
延续表示, 我们可以 lambda 抽象来构建绑定运算符应该返回的所需b
计算。