Applicative functor vs monad 在 Scala 中的组合性能

Applicative functors vs monad composing performance in Scala

我有两个 monad 实例 val a: M[A]val b: M[B]。在以下代码案例中会有任何性能差异吗?

def f: (A, B) => C

val applicativeCombine = (a |@| b)(f)

val monadCombine = 
  for {
   aa <- a
   bb <- b
  } yield f(aa, bb)

...还是取决于?

如果ab是return未来的方法并执行一些计算量大的操作,applicativeCombine版本将运行它们并行,而monadCombine 不会,这意味着 |@| 可能快两倍。

另一方面,如果 ab 是选项——甚至是未执行计算量大的工作的未来——for 版本可能更快,因为它的脱糖形式涉及更少的分配:

scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._

scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._

scala> showCode(reify((Option(1) |@| Option(2))(_ + _)).tree)
res0: String = Scalaz.ToApplyOps(Option.apply(1))(Scalaz.optionInstance).|@|(Option.apply(2)).apply(((x, x) => x.+(x)))(Scalaz.optionInstance)

scala> showCode(reify(for { a <- Option(1); b <- Option(2) } yield a + b).tree)
res1: String = Option.apply(1).flatMap(((a) => Option.apply(2).map(((b) => a.+(b)))))

(但这在大多数程序中不太可能产生有意义的差异。)

如果这是 Cats 并且 ab 是其中之一并且您有 "wrong" 导入,那么您将得到 FlatMapSyntaxEitherInstances 而不是 EitherSyntax|@| 版本可能再次更快,因为 for 版本会导致至少几个额外的 Unapply 实例和一些其他东西。

所以简短的回答是肯定的,这要视情况而定,如果您真的关心性能,则需要针对您的特定用例进行仔细的基准测试。

首选应用版本的最佳理由与性能没有任何关系,但它是您要执行的计算的更精确表示。您的 ab 不相互依赖,但 for-comprehension 表示它们相互依赖。请参阅我的回答 here 以进行更多讨论。