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

bleep.Lazy.scala Maven / Gradle / Ivy

package bleep

import scala.util.control.NonFatal

// guard against circular evaluation
// may double-compute if not synchronized on the outside
sealed trait Lazy[T] {
  protected var state: Lazy.State[T] = Lazy.State.Initial

  def get: Option[T]

  final def forceGet: T =
    get.getOrElse(sys.error(s"Unexpected circular"))

  final def forceGet(circumstance: String): T =
    get.getOrElse(sys.error(s"Unexpected circular: $circumstance"))

  final def map[U](f: T => U): Lazy[U] =
    new Lazy.Mapped(this, f)

  final def getIfEvaluated: Option[T] =
    state match {
      case Lazy.State.Initial | Lazy.State.Computing => None
      case Lazy.State.Done(value)                    => Some(value)
      case Lazy.State.Failed(th)                     => throw th
    }
}

object Lazy {
  def apply[T](compute: => T): Lazy[T] = new Lazy[T] {
    override def get: Option[T] =
      state match {
        case State.Initial =>
          state = State.Computing
          try {
            val computed = compute
            state = State.Done(computed)
            Some(computed)
          } catch {
            case NonFatal(th) =>
              state = State.Failed(th)
              throw th
          }
        case State.Computing   => None
        case State.Done(value) => Some(value)
        case State.Failed(th)  => throw th
      }
  }

  private final class Mapped[T, U](outer: Lazy[T], f: T => U) extends Lazy[U] {
    override def get: Option[U] =
      state match {
        case State.Initial =>
          outer.get.map { gotten =>
            state = State.Computing
            try {
              val computed = f(gotten)
              state = State.Done(computed)
              computed
            } catch {
              case NonFatal(th) =>
                state = State.Failed(th)
                throw th
            }
          }
        case State.Computing   => None
        case State.Done(value) => Some(value)
        case State.Failed(th)  => throw th
      }
  }

  sealed trait State[+T]
  object State {
    case object Initial extends State[Nothing]
    case object Computing extends State[Nothing]
    case class Done[T](value: T) extends State[T]
    case class Failed(th: Throwable) extends State[Nothing]
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy