我有两个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)
......还是依赖?
答案 0 :(得分:5)
如果a
和b
是返回期货并执行一些计算成本高昂的操作的方法,则applicativeCombine
版本将并行运行它们monadCombine
赢了,这意味着|@|
的速度可能接近两倍。
如果a
和b
是选项,另一方面 - 甚至是未进行计算成本高昂工作的期货 - 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而且a
和b
是eithers而你有&#34;错误&#34;导入,以便您获得FlatMapSyntax
和EitherInstances
而不是EitherSyntax
,|@|
版本可能会再次更快,因为for
版本会导致至少几个额外的Unapply
个实例和其他一些东西。
所以简短的回答是肯定的,这取决于,如果您真的关心性能,那么您需要针对您的特定用例进行仔细的基准测试。
偏好应用版本的最佳理由并不与性能有任何关系,但它表示您想要执行的计算更准确。您的a
和b
并不相互依赖,但for
- 理解他们会这样做。有关更多讨论,请参阅我的回答here。