【问题标题】:Applicative functors vs monad composing performance in ScalaScala 中的应用函子与 monad 组合性能
【发布时间】:2016-12-29 10:46:38
【问题描述】:

我有两个 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)

...还是取决于它?

【问题讨论】:

  • 您可能应该添加一个scalaz 标签,因为从问题本身并不清楚|@| 的来源。

标签: performance scala functional-programming scalaz


【解决方案1】:

如果 ab 是返回期货并执行一些计算量大的操作的方法,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$1, x$2) => x$1.+(x$2)))(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 是两者之一,并且您有“错误”的导入,那么您将得到 FlatMapSyntaxEitherInstances 而不是 EitherSyntax|@|版本可能再次更快,因为for 版本会导致至少有几个额外的Unapply 实例和其他一些东西。

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

不过,偏爱应用程序版本的最佳理由与性能没有任何关系,因为它更精确地表示了您想要执行的计算。您的 ab 不相互依赖,但 for-comprehension 说它们依赖。更多讨论见我的回答here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-02
    • 1970-01-01
    • 1970-01-01
    • 2016-10-22
    • 2019-05-15
    • 1970-01-01
    相关资源
    最近更新 更多