您好我正在尝试实施穷人的并发Monad。 这是我的代码:
import Control.Monad
data Concurrent a = Concurrent ((a -> Action) -> Action)
data Action
= Atom (IO Action)
| Fork Action Action
| Stop
instance Monad Concurrent where
(Concurrent ma) >>= f = Concurrent (\x -> ma(\y -> "Something return a Action"))
return x = Concurrent (\c -> c x)
这是我的分析:
x
的类型为b
,y
的类型为a
,f
的类型为(a -> ((b ->Action) -> Action))
。为了找出“Something return a Action”,我首先计算(f y)
,它返回((b ->Action) -> Action)
的类型。然后,如何将其与x
一起使用以生成Action?
答案 0 :(得分:21)
您正在寻找的定义类似于
Concurrent h >>= k = Concurrent (\f -> h (\x -> runConcurrent (k x) f))
我们是如何到达那里的?与往常一样,我们让类型完成工作。 :)
让我们首先介绍一个辅助函数:
runConcurrent :: Concurrent b -> (b -> Action) -> Action
runConcurrent (Concurrent h) = h
如果你从定义的左侧开始
Concurrent h >>= k = ...
使用h :: (a -> Action) -> Action
和k :: a -> Concurrent b
,那么您的目标是将...
替换为Concurrent b
类型的表达式,不是吗?
我们如何构建Concurrent b
类型的值?一种方法是应用我们的函数k
,但这不会起作用,因为我们没有合适的a
类值作为参数。所以,我们唯一能做的就是应用Concurrent
类型的数据构造函数((b -> Action) -> Action) -> Concurrent b
。
这给出了:
Concurrent h >>= k = Concurrent ...
现在我们必须找到(b -> Action) -> Action
类型的表达式作为Concurrent
的参数。我们知道函数类型的表达式总是可以通过lambda-abstraction构建:
Concurrent h >>= k = Concurrent (\f -> ...)
这为我们提供了f :: b -> Action
,并且有义务使用...
类型的表达式替换Action
。直接使用其中一个Action
- 构造函数当然会作弊;)。为了保证(>>=)
的通用性(更确切地说,为了确保我们最终遵守monad法则),我们将Action
视为抽象数据类型。然后,生成Action
- 值的唯一方法是应用函数h
:
Concurrent h >>= k = Concurrent (\f -> h ...)
因此,接下来我们需要为h
提供类型为a -> Action
的参数。这又是一个函数类型,所以我们抛出另一个lambda:
Concurrent h >>= k = Concurrent (\f -> h (\x -> ...))
因此,我们有x :: a
并需要构建Action
类型的主体。我们可以使用a
类型的值做什么?我们可以将它提供给函数k
。这为我们提供了Concurrent b
类型的值,然后我们可以将其传递给辅助函数runConcurrent
:
Concurrent h >>= k = Concurrent (\f -> h (\x -> runConcurrent (k x) ...))
这为我们提供了(b -> Action) -> Action
类型的函数,并提供f
作为参数可以解决问题:
Concurrent h >>= k = Concurrent (\f -> h (\x -> runConcurrent (k x) f))