这里有一些值得深思的地方。
当我编写 monadic 代码时,monad 会对完成的操作进行排序。例如,如果我在 IO monad 中编写:
do a <- doSomething
b <- doSomethingElse
return (a + b)
我知道
doSomething
将在 doSomethingElse
之前执行.现在,考虑类似 C 语言的等效代码:
return (doSomething() + doSomethingElse());
C 的语义实际上并没有指定这两个函数调用的评估顺序,因此编译器可以随意移动。
我的问题是:我将如何在 Haskell 中编写单子(monad)代码,这也使这个评估顺序未定义? 理想情况下,当我的编译器的优化器查看代码并开始移动时,我会获得一些好处。
以下是一些可能无法完成工作但具有正确“精神”的技术:
plus doSomething doSomethingElse
让 plus
安排单子(monad)调用。缺点:您无法共享单子(monad)操作的结果,并且 plus
仍然会决定何时对事物进行评估。 unsafeInterleaveIO
,这将调度推迟到评估懒惰的需求。但是惰性与未定义顺序的严格不同:特别是我确实希望我的所有一元 Action 都能被执行。 seq
不施加排序约束。 从这个意义上说,我想要一些比一元排序更灵活但不如完全惰性的东西。
最佳答案
这个过度序列化 monad 代码的问题被称为 "commutative monads problem" .
Commutative monads are monads for which the order of actions makes no difference (they commute), that is when following code:
do a <- f x
b <- g y
m a b
是相同的:
do b <- g y
a <- f x
m a b
有许多通勤的单子(monad)(例如
Maybe
, Random
)。例如,如果 monad 是可交换的,那么在其中捕获的操作可以并行计算。它们非常有用!但是,我们 对于通勤的 monad 没有良好的语法 , 虽然 a lot of people have asked对于这样的事情——它仍然是 open research problem .
顺便说一句,应用仿函数确实给了我们重新排序计算的自由,但是,你必须放弃
bind
的概念。 (作为建议,例如 liftM2
显示)。
关于haskell - 放松一元计算中的排序约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5897845/