diff --git a/bench/src/main/scala/cats/bench/FoldBench.scala b/bench/src/main/scala/cats/bench/FoldBench.scala index 08a28c3549..d6388e868e 100644 --- a/bench/src/main/scala/cats/bench/FoldBench.scala +++ b/bench/src/main/scala/cats/bench/FoldBench.scala @@ -12,11 +12,13 @@ class FoldBench { val chars: List[String] = ('a' to 'z').map(_.toString).toList /** Benchmark fold of Foldable[List] */ - @Benchmark def fold(): String = + @Benchmark + def fold(): String = Foldable[List].fold(chars) /** Benchmark fold using traverse with Const */ - @Benchmark def traverseConst(): String = + @Benchmark + def traverseConst(): String = Traverse[List].traverse[Const[String, ?], String, String](chars)(Const(_)).getConst } diff --git a/bench/src/main/scala/cats/bench/TrampolineBench.scala b/bench/src/main/scala/cats/bench/TrampolineBench.scala new file mode 100644 index 0000000000..2efbe0f1eb --- /dev/null +++ b/bench/src/main/scala/cats/bench/TrampolineBench.scala @@ -0,0 +1,45 @@ +package cats.bench + +import org.openjdk.jmh.annotations.{Benchmark, Scope, State} + +import cats._ +import cats.implicits._ +import cats.free.Trampoline + +import scala.util.control.TailCalls + +@State(Scope.Benchmark) +class TrampolineBench { + + val N = 15 + + @Benchmark + def eval(): Int = evalFib(N).value + + def evalFib(n: Int): Eval[Int] = + if (n < 2) Eval.now(n) else for { + x <- Eval.defer(evalFib(n - 1)) + y <- Eval.defer(evalFib(n - 2)) + } yield x + y + + + @Benchmark + def trampoline(): Int = trampolineFib(N).run + + def trampolineFib(n: Int): Trampoline[Int] = + if (n < 2) Trampoline.done(n) else for { + x <- Trampoline.suspend(trampolineFib(n - 1)) + y <- Trampoline.suspend(trampolineFib(n - 2)) + } yield x + y + + // TailRec[A] only has .flatMap in 2.11. + + // @Benchmark + // def stdlib(): Int = stdlibFib(N).result + // + // def stdlibFib(n: Int): TailCalls.TailRec[Int] = + // if (n < 2) TailCalls.done(n) else for { + // x <- TailCalls.tailcall(stdlibFib(n - 1)) + // y <- TailCalls.tailcall(stdlibFib(n - 2)) + // } yield x + y +} diff --git a/build.sbt b/build.sbt index bff9d42636..2724c6699e 100644 --- a/build.sbt +++ b/build.sbt @@ -1,7 +1,6 @@ import com.typesafe.sbt.pgp.PgpKeys.publishSigned import com.typesafe.sbt.SbtSite.SiteKeys._ import com.typesafe.sbt.SbtGhPages.GhPagesKeys._ -import pl.project13.scala.sbt.SbtJmh._ import sbtunidoc.Plugin.UnidocKeys._ import ReleaseTransformations._ import ScoverageSbtPlugin._ @@ -189,8 +188,8 @@ lazy val bench = project.dependsOn(macrosJVM, coreJVM, freeJVM, lawsJVM) .settings(moduleName := "cats-bench") .settings(catsSettings) .settings(noPublishSettings) - .settings(jmhSettings) .settings(commonJvmSettings) + .enablePlugins(JmhPlugin) // cats-js is JS-only lazy val js = project diff --git a/core/src/main/scala/cats/Eval.scala b/core/src/main/scala/cats/Eval.scala index 709de3fcb7..b46a9b9c01 100644 --- a/core/src/main/scala/cats/Eval.scala +++ b/core/src/main/scala/cats/Eval.scala @@ -82,6 +82,12 @@ sealed abstract class Eval[A] { self => val run = f } } + case c: Eval.Call[A] => + new Eval.Compute[B] { + type Start = A + val start = c.thunk + val run = f + } case _ => new Eval.Compute[B] { type Start = A @@ -194,7 +200,7 @@ object Eval extends EvalInstances { * which produces an Eval[A] value. Like .flatMap, it is stack-safe. */ def defer[A](a: => Eval[A]): Eval[A] = - Eval.Unit.flatMap(_ => a) + new Eval.Call[A](a _) {} /** * Static Eval instances for some common values. @@ -208,6 +214,18 @@ object Eval extends EvalInstances { val Zero: Eval[Int] = Now(0) val One: Eval[Int] = Now(1) + /** + * Call is a type of Eval[A] that is used to defer computations + * which produce Eval[A]. + * + * Users should not instantiate Call instances themselves. Instead, + * they will be automatically created when needed. + */ + sealed abstract class Call[A](val thunk: () => Eval[A]) extends Eval[A] { + def memoize: Eval[A] = new Later(() => thunk().value) + def value: A = thunk().value + } + /** * Compute is a type of Eval[A] that is used to chain computations * involving .map and .flatMap. Along with Eval#flatMap it diff --git a/project/plugins.sbt b/project/plugins.sbt index 87da7b4a55..a61f92053c 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -9,7 +9,7 @@ addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.5.3") addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.8.1") addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.4.0") -addSbtPlugin("pl.project13.scala"% "sbt-jmh" % "0.1.10") +addSbtPlugin("pl.project13.scala"% "sbt-jmh" % "0.2.3") addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.6.0") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.2.0") addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.4")