mirror of https://github.com/sbt/sbt.git
commit
bc059ad7bb
|
|
@ -1,296 +0,0 @@
|
|||
package sbt.util
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
// Copied from Cats (MIT license)
|
||||
|
||||
/**
|
||||
* 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.Compute[A] =>
|
||||
new Eval.Compute[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] {
|
||||
type Start = A
|
||||
val start = () => c.run(s)
|
||||
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
|
||||
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 {
|
||||
|
||||
/**
|
||||
* 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.Call[A](a _) {}
|
||||
|
||||
/**
|
||||
* Static Eval instances for some common values.
|
||||
*
|
||||
* These can be useful in cases where the same values may be needed
|
||||
* many times.
|
||||
*/
|
||||
val Unit: Eval[Unit] = Now(())
|
||||
val True: Eval[Boolean] = Now(true)
|
||||
val False: Eval[Boolean] = Now(false)
|
||||
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(() => value)
|
||||
def value: A = Call.loop(this).value
|
||||
}
|
||||
|
||||
object Call {
|
||||
/** Collapse the call stack for eager evaluations */
|
||||
@tailrec private def loop[A](fa: Eval[A]): Eval[A] = fa match {
|
||||
case call: Eval.Call[A] =>
|
||||
loop(call.thunk())
|
||||
case compute: Eval.Compute[A] =>
|
||||
new Eval.Compute[A] {
|
||||
type Start = compute.Start
|
||||
val start: () => Eval[Start] = () => compute.start()
|
||||
val run: Start => Eval[A] = s => loop1(compute.run(s))
|
||||
}
|
||||
case other => other
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for loop that can be called in a non-tail position
|
||||
* from an otherwise tailrec-optimized loop.
|
||||
*/
|
||||
private def loop1[A](fa: Eval[A]): Eval[A] = loop(fa)
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute 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
|
||||
* 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] {
|
||||
type Start
|
||||
val start: () => Eval[Start]
|
||||
val run: Start => Eval[A]
|
||||
|
||||
def memoize: Eval[A] = Later(value)
|
||||
|
||||
def value: A = {
|
||||
type L = Eval[Any]
|
||||
type C = Any => Eval[Any]
|
||||
@tailrec def loop(curr: L, fs: List[C]): Any =
|
||||
curr match {
|
||||
case c: Compute[_] =>
|
||||
c.start() match {
|
||||
case cc: Compute[_] =>
|
||||
loop(
|
||||
cc.start().asInstanceOf[L],
|
||||
cc.run.asInstanceOf[C] :: c.run.asInstanceOf[C] :: fs
|
||||
)
|
||||
case xx =>
|
||||
loop(c.run(xx.value), fs)
|
||||
}
|
||||
case x =>
|
||||
fs match {
|
||||
case f :: fs => loop(f(x.value), fs)
|
||||
case Nil => x.value
|
||||
}
|
||||
}
|
||||
loop(this.asInstanceOf[L], Nil).asInstanceOf[A]
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue