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)
...还是取决于?
如果a
和b
是return未来的方法并执行一些计算量大的操作,applicativeCombine
版本将运行它们并行,而monadCombine
不会,这意味着 |@|
可能快两倍。
另一方面,如果 a
和 b
是选项——甚至是未执行计算量大的工作的未来——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 并且 a
和 b
是其中之一并且您有 "wrong" 导入,那么您将得到 FlatMapSyntax
和 EitherInstances
而不是 EitherSyntax
,|@|
版本可能再次更快,因为 for
版本会导致至少几个额外的 Unapply
实例和一些其他东西。
所以简短的回答是肯定的,这要视情况而定,如果您真的关心性能,则需要针对您的特定用例进行仔细的基准测试。
首选应用版本的最佳理由与性能没有任何关系,但它是您要执行的计算的更精确表示。您的 a
和 b
不相互依赖,但 for
-comprehension 表示它们相互依赖。请参阅我的回答 here 以进行更多讨论。
我有两个 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)
...还是取决于?
如果a
和b
是return未来的方法并执行一些计算量大的操作,applicativeCombine
版本将运行它们并行,而monadCombine
不会,这意味着 |@|
可能快两倍。
另一方面,如果 a
和 b
是选项——甚至是未执行计算量大的工作的未来——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 并且 a
和 b
是其中之一并且您有 "wrong" 导入,那么您将得到 FlatMapSyntax
和 EitherInstances
而不是 EitherSyntax
,|@|
版本可能再次更快,因为 for
版本会导致至少几个额外的 Unapply
实例和一些其他东西。
所以简短的回答是肯定的,这要视情况而定,如果您真的关心性能,则需要针对您的特定用例进行仔细的基准测试。
首选应用版本的最佳理由与性能没有任何关系,但它是您要执行的计算的更精确表示。您的 a
和 b
不相互依赖,但 for
-comprehension 表示它们相互依赖。请参阅我的回答 here 以进行更多讨论。