Haskell:并行计算和 monad 的“顺序属性”

Saf*_*ron 6 monads haskell repa

我很困惑为什么 REPA 函数computeP将其结果打包在一个 monad 中。它具有以下类型签名。

computeP :: (Load r1 sh e, Target r2 e, Source r2 e, Monad m) =>
            Array r1 sh e -> m (Array r2 sh e)
Run Code Online (Sandbox Code Playgroud)

本教程中它说

这样做的原因是monad 给出了一个明确定义的序列概念,因此计算 P 强制在 monadic 计算的特定点完成并行评估。

同样,Stack Overflow 上的这个答案指出

Repa 中的并行计算必须是 monadic 的原因部分与懒惰有关,但主要是因为 Repa 无法处理嵌套并行性。Monad 的顺序属性在很大程度上解决了这个问题[.]

问题

  • 拥有这个“顺序属性”究竟意味着什么?
  • 一个 monad 如何执行这个?
  • 例如computeP:对使用哪个 monad 没有限制,所以我可以使用身份 monad。那么可以使用下面的函数来代替解包 monad,还是会因为它缺少这个顺序属性而产生意想不到的结果?如果可以的话,甚至有必要使用 monad 吗?

    import Data.Functor.Identity
    import Data.Array.Repa.Eval
    import Data.Array.Repa
    
    myComputeP :: (Load r1 sh e, Target r2 e, Source r2 e) => Array r1 sh e -> Array r2 sh e
    myComputeP = runIdentity . computeP
    
    Run Code Online (Sandbox Code Playgroud)

任何帮助都会很棒。

Li-*_*Xia 2

这个单子约束是一种启发式技巧。它可以帮助有纪律的用户避免嵌套并行,但对恶意或无知的用户没有任何作用。

嵌套并行是指在并行计算某个数组时,最终必须并行计算另一个数组的情况。Repa不支持它(原因并不重要),所以它尽量避免它。

的类型computeP有助于确保并行计算彼此按顺序完成,但它远非无懈可击;它只是一个“尽力而为”的抽象。

Monad 如何强制执行此操作?

实际上,computeP仅适用于其第一个参数绑定(>>=)严格的 monad,因此在 中u >>= k,该函数仅在评估k后才会应用。那么如果你与这样的 monad 一起u使用,computeP

do w <- computeP v
   k w
Run Code Online (Sandbox Code Playgroud)

确保向量w在传递给 之前被评估k,这可以安全地执行其他computeP操作。

  • 严格单子的示例:IO、 strict StateMaybe[]
  • 惰性单子的示例:Identity、惰性StateReader。(惰性 monad 可以变得严格,但反之则不然。特别是,如果您只想进行 Repa 计算,则可以定义严格的恒等 monad。)

为了防止嵌套并行性, 的类型computeP故意使其在可能并行完成的操作中使用起来很麻烦,例如采用非单子函数的map :: (a -> b) -> Array _ _ a -> Array _ _ b和。fromFunction :: sh -> (sh -> a) -> Array _ _ a人们仍然可以明确地打开computeP,例如,正如runIdentity您所注意到的:如果您愿意,您可以朝自己的脚开枪,但需要您来装载枪,将其指向下方并扣动扳机。


希望这能回答 Repa 正在发生的事情。以下是回答另一个问题的理论题外话:

拥有这个“顺序属性”到底意味着什么?

这些引文相当手气。在我看来,“顺序性”和“单子”之间的关系是双重的。

首先,对于 Haskell 中的许多 monad, 的定义(>>=)自然决定了求值的顺序,通常是因为它立即与第一个参数进行模式匹配。正如前面所解释的,这就是 Repa 所依赖的强制computeP计算按顺序发生的原因(这就是为什么如果你将它专门化为 的话它会中断Identity;它不是一个严格的 monad)。从总体上看,这是惰性求值的一个相当小的细节,而不是一般而言适合 monad 的任何东西。

其次,纯函数式编程的一个核心思想是一流的计算,具有效果和组合的明确定义。在这种情况下,效果是向量的并行计算,而我们关心的组合是顺序组合。Monad 为顺序组合提供了通用模型或接口。这也是 monad 帮助解决 Repa 中避免嵌套并行问题的部分原因。

重点不是单子具有固有的顺序方面,而是顺序组合本质上是单子:如果您尝试列出您期望从任何名副其实的“顺序组合”名称中获得的一般属性,那么您很可能会结束一起被称为“monad”的属性;这是 Moggi 的开创性论文“计算和单子的概念”的要点之一。

“Monad”并不是一个神奇的概念,而是非常普遍的,所以很多东西碰巧都是单子。毕竟,主要要求是存在关联操作;这是关于顺序组合的一个非常合理的假设。(如果,当您听到“结合性”时,您会想到“幺半群”或“类别”,请注意,所有这些概念都统一在“幺半群对象”的保护伞下,因此就“结合性”而言,它们是都是相同的想法,只是属于不同的类别。)