应用仿函数vs monad在Scala中的表现

时间:2016-12-29 10:46:38

标签: performance scala functional-programming scalaz

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

......还是依赖?

1 个答案:

答案 0 :(得分:5)

如果ab是返回期货并执行一些计算成本高昂的操作的方法,则applicativeCombine版本将并行运行它们monadCombine赢了,这意味着|@|的速度可能接近两倍。

如果ab是选项,另一方面 - 甚至是未进行计算成本高昂工作的期货 - for可能版本可能会更快,因为它的desugared形式涉及更少的分配:

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是eithers而你有&#34;错误&#34;导入,以便您获得FlatMapSyntaxEitherInstances而不是EitherSyntax|@|版本可能会再次更快,因为for版本会导致至少几个额外的Unapply个实例和其他一些东西。

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

偏好应用版本的最佳理由并不与性能有任何关系,但它表示您想要执行的计算更准确。您的ab并不相互依赖,但for - 理解他们会这样做。有关更多讨论,请参阅我的回答here