All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cats.Eval.scala Maven / Gradle / Ivy

The newest version!
package cats

import scala.annotation.tailrec

import cats.syntax.all._

/**
 * Eval is a monad which controls evaluation.
 *
 * This type wraps a value (or a computation that produces a value)
 * and can produce it on command via the `.value` method.
 *
 * There are three basic evaluation strategies:
 *
 *  - Now:    evaluated immediately
 *  - Later:  evaluated once when value is needed
 *  - Always: evaluated every time value is needed
 *
 * The Later and Always are both lazy strategies while Now is eager.
 * Later and Always are distinguished from each other only by
 * memoization: once evaluated Later will save the value to be returned
 * immediately if it is needed again. Always will run its computation
 * every time.
 *
 * Eval supports stack-safe lazy computation via the .map and .flatMap
 * methods, which use an internal trampoline to avoid stack overflows.
 * Computation done within .map and .flatMap is always done lazily,
 * even when applied to a Now instance.
 *
 * It is not generally good style to pattern-match on Eval instances.
 * Rather, use .map and .flatMap to chain computation, and use .value
 * to get the result when needed. It is also not good style to create
 * Eval instances whose computation involves calling .value on another
 * Eval instance -- this can defeat the trampolining and lead to stack
 * overflows.
 */
sealed abstract class Eval[+A] extends Serializable { self =>

  /**
   * Evaluate the computation and return an A value.
   *
   * For lazy instances (Later, Always), any necessary computation
   * will be performed at this point. For eager instances (Now), a
   * value will be immediately returned.
   */
  def value: A

  /**
   * Transform an Eval[A] into an Eval[B] given the transformation
   * function `f`.
   *
   * This call is stack-safe -- many .map calls may be chained without
   * consumed additional stack during evaluation.
   *
   * Computation performed in f is always lazy, even when called on an
   * eager (Now) instance.
   */
  def map[B](f: A => B): Eval[B] =
    flatMap(a => Now(f(a)))

  /**
   * Lazily perform a computation based on an Eval[A], using the
   * function `f` to produce an Eval[B] given an A.
   *
   * This call is stack-safe -- many .flatMap calls may be chained
   * without consumed additional stack during evaluation. It is also
   * written to avoid left-association problems, so that repeated
   * calls to .flatMap will be efficiently applied.
   *
   * Computation performed in f is always lazy, even when called on an
   * eager (Now) instance.
   */
  def flatMap[B](f: A => Eval[B]): Eval[B] =
    this match {
      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.FlatMap[B] {
              type Start = A
              val start = () => c.run(s)
              val run = f
            }
        }
      case c: Eval.Defer[A] =>
        new Eval.FlatMap[B] {
          type Start = A
          val start = c.thunk
          val run = f
        }
      case _ =>
        new Eval.FlatMap[B] {
          type Start = A
          val start = () => self
          val run = f
        }
    }

  /**
   * Ensure that the result of the computation (if any) will be
   * memoized.
   *
   * Practically, this means that when called on an Always[A] a
   * Later[A] with an equivalent computation will be returned.
   */
  def memoize: Eval[A]
}

/**
 * Construct an eager Eval[A] instance.
 *
 * In some sense it is equivalent to using a val.
 *
 * This type should be used when an A value is already in hand, or
 * when the computation to produce an A value is pure and very fast.
 */
final case class Now[A](value: A) extends Eval[A] {
  def memoize: Eval[A] = this
}

/**
 * Construct a lazy Eval[A] instance.
 *
 * This type should be used for most "lazy" values. In some sense it
 * is equivalent to using a lazy val.
 *
 * When caching is not required or desired (e.g. if the value produced
 * may be large) prefer Always. When there is no computation
 * necessary, prefer Now.
 *
 * Once Later has been evaluated, the closure (and any values captured
 * by the closure) will not be retained, and will be available for
 * garbage collection.
 */
final class Later[A](f: () => A) extends Eval[A] {
  private[this] var thunk: () => A = f

  // The idea here is that `f` may have captured very large
  // structures, but produce a very small result. In this case, once
  // we've calculated a value, we would prefer to be able to free
  // everything else.
  //
  // (For situations where `f` is small, but the output will be very
  // expensive to store, consider using `Always`.)
  lazy val value: A = {
    val result = thunk()
    thunk = null // scalastyle:off
    result
  }

  def memoize: Eval[A] = this
}

object Later {
  def apply[A](a: => A): Later[A] = new Later(() => a)
}

/**
 * Construct a lazy Eval[A] instance.
 *
 * This type can be used for "lazy" values. In some sense it is
 * equivalent to using a Function0 value.
 *
 * This type will evaluate the computation every time the value is
 * required. It should be avoided except when laziness is required and
 * caching must be avoided. Generally, prefer Later.
 */
final class Always[A](f: () => A) extends Eval[A] {
  def value: A = f()
  def memoize: Eval[A] = new Later(f)
}

object Always {
  def apply[A](a: => A): Always[A] = new Always(() => a)
}

object Eval extends EvalInstances {

  /**
   * Construct an eager Eval[A] value (i.e. Now[A]).
   */
  def now[A](a: A): Eval[A] = Now(a)

  /**
   * Construct a lazy Eval[A] value with caching (i.e. Later[A]).
   */
  def later[A](a: => A): Eval[A] = new Later(() => a)

  /**
   * Construct a lazy Eval[A] value without caching (i.e. Always[A]).
   */
  def always[A](a: => A): Eval[A] = new Always(() => a)

  /**
   * Defer a computation which produces an Eval[A] value.
   *
   * This is useful when you want to delay execution of an expression
   * which produces an Eval[A] value. Like .flatMap, it is stack-safe.
   */
  def defer[A](a: => Eval[A]): Eval[A] =
    new Eval.Defer[A](() => a) {}

  /**
   * Static Eval instance for common value `Unit`.
   *
   * This can be useful in cases where the same value may be needed
   * many times.
   */
  val Unit: Eval[Unit] = Now(())

  /**
   * Static Eval instance for common value `true`.
   *
   * This can be useful in cases where the same value may be needed
   * many times.
   */
  val True: Eval[Boolean] = Now(true)

  /**
   * Static Eval instance for common value `false`.
   *
   * This can be useful in cases where the same value may be needed
   * many times.
   */
  val False: Eval[Boolean] = Now(false)

  /**
   * Static Eval instance for common value `0`.
   *
   * This can be useful in cases where the same value may be needed
   * many times.
   */
  val Zero: Eval[Int] = Now(0)

  /**
   * Static Eval instance for common value `1`.
   *
   * This can be useful in cases where the same value may be needed
   * many times.
   */
  val One: Eval[Int] = Now(1)

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

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

  /**
   * 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 advance[A](fa: Eval[A]): Eval[A] =
    fa match {
      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 => advance1(compute.run(s))
        }
      case other => other
    }

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

  /**
   * 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 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 FlatMap[A] extends Eval[A] { self =>
    type Start
    val start: () => Eval[Start]
    val run: Start => Eval[A]

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

  private case class Memoize[A](eval: Eval[A]) extends Eval[A] {
    var result: Option[A] = None
    def memoize: Eval[A] = this
    def value: A =
      result match {
        case Some(a) => a
        case None =>
          val a = evaluate(this)
          result = Some(a)
          a
      }
  }

  private def evaluate[A](e: Eval[A]): A = {
    type L = Eval[Any]
    type M = Memoize[Any]
    type C = Any => Eval[Any]

    def addToMemo(m: M): C = { (a: Any) =>
      m.result = Some(a)
      Now(a)
    }

    @tailrec def loop(curr: L, fs: List[C]): Any =
      curr match {
        case c: FlatMap[_] =>
          c.start() match {
            case cc: FlatMap[_] =>
              loop(cc.start().asInstanceOf[L], cc.run.asInstanceOf[C] :: c.run.asInstanceOf[C] :: fs)
            case mm @ Memoize(eval) =>
              mm.result match {
                case Some(a) =>
                  loop(Now(a), c.run.asInstanceOf[C] :: fs)
                case None =>
                  loop(eval, addToMemo(mm.asInstanceOf[M]) :: c.run.asInstanceOf[C] :: fs)
              }
            case xx =>
              loop(c.run(xx.value), fs)
          }
        case call: Defer[_] =>
          loop(advance(call), fs)
        case m @ Memoize(eval) =>
          m.result match {
            case Some(a) =>
              fs match {
                case f :: fs => loop(f(a), fs)
                case Nil     => a
              }
            case None =>
              loop(eval, addToMemo(m) :: fs)
          }
        case x =>
          fs match {
            case f :: fs => loop(f(x.value), fs)
            case Nil     => x.value
          }
      }

    loop(e.asInstanceOf[L], Nil).asInstanceOf[A]
  }
}

sealed abstract private[cats] class EvalInstances extends EvalInstances0 {

  implicit val catsBimonadForEval: Bimonad[Eval] with CommutativeMonad[Eval] =
    new Bimonad[Eval] with StackSafeMonad[Eval] with CommutativeMonad[Eval] {
      override def map[A, B](fa: Eval[A])(f: A => B): Eval[B] = fa.map(f)
      def pure[A](a: A): Eval[A] = Now(a)
      def flatMap[A, B](fa: Eval[A])(f: A => Eval[B]): Eval[B] = fa.flatMap(f)
      def extract[A](la: Eval[A]): A = la.value
      def coflatMap[A, B](fa: Eval[A])(f: Eval[A] => B): Eval[B] = Later(f(fa))
      override def unit: Eval[Unit] = Eval.Unit
    }

  implicit val catsDeferForEval: Defer[Eval] =
    new Defer[Eval] {
      def defer[A](e: => Eval[A]): Eval[A] =
        Eval.defer(e)
    }

  implicit val catsReducibleForEval: Reducible[Eval] =
    new Reducible[Eval] {
      def foldLeft[A, B](fa: Eval[A], b: B)(f: (B, A) => B): B =
        f(b, fa.value)
      def foldRight[A, B](fa: Eval[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
        fa.flatMap(f(_, lb))

      override def reduce[A](fa: Eval[A])(implicit A: Semigroup[A]): A =
        fa.value
      override def reduceLeft[A](fa: Eval[A])(f: (A, A) => A): A =
        fa.value
      def reduceLeftTo[A, B](fa: Eval[A])(f: A => B)(g: (B, A) => B): B =
        f(fa.value)
      override def reduceRight[A](fa: Eval[A])(f: (A, Eval[A]) => Eval[A]): Eval[A] =
        fa
      def reduceRightTo[A, B](fa: Eval[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] =
        fa.map(f)
      override def reduceRightOption[A](fa: Eval[A])(f: (A, Eval[A]) => Eval[A]): Eval[Option[A]] =
        fa.map(Some(_))
      override def reduceRightToOption[A, B](fa: Eval[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[Option[B]] =
        fa.map { a =>
          Some(f(a))
        }
      override def size[A](f: Eval[A]): Long = 1L
    }

  implicit def catsOrderForEval[A: Order]: Order[Eval[A]] =
    new Order[Eval[A]] {
      def compare(lx: Eval[A], ly: Eval[A]): Int =
        lx.value.compare(ly.value)
    }

  implicit def catsGroupForEval[A: Group]: Group[Eval[A]] =
    new EvalGroup[A] { val algebra: Group[A] = Group[A] }

  implicit val catsRepresentableForEval: Representable.Aux[Eval, Unit] = new Representable[Eval] {
    override type Representation = Unit

    override val F: Functor[Eval] = Functor[Eval]

    /**
     * Create a function that "indexes" into the `F` structure using `Representation`
     */
    override def index[A](f: Eval[A]): Unit => A = (_: Unit) => f.value

    /**
     * Reconstructs the `F` structure using the index function
     */
    override def tabulate[A](f: Unit => A): Eval[A] = Eval.later(f(()))
  }
}

sealed abstract private[cats] class EvalInstances0 extends EvalInstances1 {
  implicit def catsPartialOrderForEval[A: PartialOrder]: PartialOrder[Eval[A]] =
    new PartialOrder[Eval[A]] {
      def partialCompare(lx: Eval[A], ly: Eval[A]): Double =
        lx.value.partialCompare(ly.value)
    }

  implicit def catsMonoidForEval[A: Monoid]: Monoid[Eval[A]] =
    new EvalMonoid[A] { val algebra = Monoid[A] }
}

sealed abstract private[cats] class EvalInstances1 {
  implicit def catsEqForEval[A: Eq]: Eq[Eval[A]] =
    new Eq[Eval[A]] {
      def eqv(lx: Eval[A], ly: Eval[A]): Boolean =
        lx.value === ly.value
    }

  implicit def catsSemigroupForEval[A: Semigroup]: Semigroup[Eval[A]] =
    new EvalSemigroup[A] { val algebra = Semigroup[A] }
}

trait EvalSemigroup[A] extends Semigroup[Eval[A]] {
  implicit def algebra: Semigroup[A]
  def combine(lx: Eval[A], ly: Eval[A]): Eval[A] =
    for { x <- lx; y <- ly } yield x |+| y
}

trait EvalMonoid[A] extends Monoid[Eval[A]] with EvalSemigroup[A] {
  implicit def algebra: Monoid[A]
  lazy val empty: Eval[A] = Eval.later(algebra.empty)
}

trait EvalGroup[A] extends Group[Eval[A]] with EvalMonoid[A] {
  implicit def algebra: Group[A]
  def inverse(lx: Eval[A]): Eval[A] =
    lx.map(_.inverse())
  override def remove(lx: Eval[A], ly: Eval[A]): Eval[A] =
    for { x <- lx; y <- ly } yield x |-| y
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy