Skip to content

Commit

Permalink
Rename Compute to FlatMap and Call to Defer.
Browse files Browse the repository at this point in the history
  • Loading branch information
erik-stripe committed Sep 3, 2017
1 parent 12d6fd0 commit c9572b2
Showing 1 changed file with 33 additions and 29 deletions.
62 changes: 33 additions & 29 deletions core/src/main/scala/cats/Eval.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,28 +72,28 @@ sealed abstract class Eval[+A] extends Serializable { self =>
*/
def flatMap[B](f: A => Eval[B]): Eval[B] =
this match {
case c: Eval.Compute[A] =>
new Eval.Compute[B] {
case c: Eval.FlatMap[A] =>
new Eval.FlatMap[B] {
type Start = c.Start
// See https://issues.scala-lang.org/browse/SI-9931 for an explanation
// of why the type annotations are necessary in these two lines on
// Scala 2.12.0.
val start: () => Eval[Start] = c.start
val run: Start => Eval[B] = (s: c.Start) =>
new Eval.Compute[B] {
new Eval.FlatMap[B] {
type Start = A
val start = () => c.run(s)
val run = f
}
}
case c: Eval.Call[A] =>
new Eval.Compute[B] {
case c: Eval.Defer[A] =>
new Eval.FlatMap[B] {
type Start = A
val start = c.thunk
val run = f
}
case _ =>
new Eval.Compute[B] {
new Eval.FlatMap[B] {
type Start = A
val start = () => self
val run = f
Expand Down Expand Up @@ -203,7 +203,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] =
new Eval.Call[A](a _) {}
new Eval.Defer[A](a _) {}

/**
* Static Eval instance for common value `Unit`.
Expand Down Expand Up @@ -246,56 +246,60 @@ object Eval extends EvalInstances {
val One: Eval[Int] = Now(1)

/**
* Call is a type of Eval[A] that is used to defer computations
* Defer is a type of Eval[A] that is used to defer computations
* which produce Eval[A].
*
* Users should not instantiate Call instances themselves. Instead,
* Users should not instantiate Defer instances themselves. Instead,
* they will be automatically created when needed.
*/
sealed abstract class Call[A](val thunk: () => Eval[A]) extends Eval[A] {
sealed abstract class Defer[A](val thunk: () => Eval[A]) extends Eval[A] {

def memoize: Eval[A] = Memoize(this)
def value: A = evaluate(this)
}

/**
* Collapse the call stack for eager evaluations.
* returns a non Call Eval node
* Advance until we find a non-deferred Eval node.
*
* Often we may have deep chains of Defer nodes; the goal here is to
* advance through those to find the underlying "work" (in the case
* of FlatMap nodes) or "value" (in the case of Now, Later, or
* Always nodes).
*/
@tailrec private def doCall[A](fa: Eval[A]): Eval[A] =
@tailrec private def advance[A](fa: Eval[A]): Eval[A] =
fa match {
case call: Eval.Call[A] =>
doCall(call.thunk())
case compute: Eval.Compute[A] =>
new Eval.Compute[A] {
case call: Eval.Defer[A] =>
advance(call.thunk())
case compute: Eval.FlatMap[A] =>
new Eval.FlatMap[A] {
type Start = compute.Start
val start: () => Eval[Start] = () => compute.start()
val run: Start => Eval[A] = s => doCall1(compute.run(s))
val run: Start => Eval[A] = s => advance1(compute.run(s))
}
case other => other
}

/**
* Alias for doCall that can be called in a non-tail position
* from an otherwise tailrec-optimized doCall.
* Alias for advance that can be called in a non-tail position
* from an otherwise tailrec-optimized advance.
*/
private def doCall1[A](fa: Eval[A]): Eval[A] =
doCall(fa)
private def advance1[A](fa: Eval[A]): Eval[A] =
advance(fa)

/**
* Compute is a type of Eval[A] that is used to chain computations
* FlatMap is a type of Eval[A] that is used to chain computations
* involving .map and .flatMap. Along with Eval#flatMap it
* implements the trampoline that guarantees stack-safety.
*
* Users should not instantiate Compute instances
* Users should not instantiate FlatMap instances
* themselves. Instead, they will be automatically created when
* needed.
*
* Unlike a traditional trampoline, the internal workings of the
* trampoline are not exposed. This allows a slightly more efficient
* implementation of the .value method.
*/
sealed abstract class Compute[A] extends Eval[A] { self =>
sealed abstract class FlatMap[A] extends Eval[A] { self =>
type Start
val start: () => Eval[Start]
val run: Start => Eval[A]
Expand Down Expand Up @@ -330,9 +334,9 @@ object Eval extends EvalInstances {

@tailrec def loop(curr: L, fs: List[C]): Any =
curr match {
case c: Compute[_] =>
case c: FlatMap[_] =>
c.start() match {
case cc: Compute[_] =>
case cc: FlatMap[_] =>

This comment has been minimized.

Copy link
@johnynek

johnynek Sep 3, 2017

Contributor

I think this is never the case by construction. Look where we make start, and I think it is never flatMap.

loop(
cc.start().asInstanceOf[L],
cc.run.asInstanceOf[C] :: c.run.asInstanceOf[C] :: fs)
Expand All @@ -346,8 +350,8 @@ object Eval extends EvalInstances {
case xx =>
loop(c.run(xx.value), fs)
}
case call: Call[_] =>
loop(doCall(call), fs)
case call: Defer[_] =>
loop(advance(call), fs)
case m@Memoize(eval) =>
m.result match {
case Some(a) =>
Expand Down

0 comments on commit c9572b2

Please sign in to comment.