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

org.opalj.Result.scala Maven / Gradle / Ivy

The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj

/**
 * Represents the result of some expression that either (a) succeeded and encapsulates some value,
 * or (b) finished, but has no value - because it was not possible to compute a value using the
 * given/available information - or (c) that failed.
 *
 * @note    Depending on the context, it may be useful to distinguish between a success that returns
 *          an empty collection and a success that has no further information.
 *
 * @author  Michael Eichberg
 */
// TODO Create specialized IntResult and RefResult classes
sealed trait Result[@specialized(Int) +T] extends Serializable {
    final def isEmpty: Boolean = !hasValue
    def hasValue: Boolean
    def value: T
    def map[B](f: T ⇒ B): Result[B]
    def flatMap[B](f: T ⇒ Result[B]): Result[B]
    def foreach[U](f: (T) ⇒ U): Unit
    def withFilter(q: (T) ⇒ Boolean): Result[T]
    def toSet[X >: T]: Set[X]
    def toOption: Option[T]
}

/**
 * Defines factory methods for [[Result]] objects.
 *
 * @author Michael Eichberg
 */
object Result {

    /**
     * Maps a `Some` to [[Success]] and `None` to [[Empty$]].
     */
    def apply[T](result: Option[T]): Result[T] = {
        result match {
            case Some(value) ⇒ Success(value)
            case _ /*None*/  ⇒ Empty
        }
    }

    def successOrFailure[T](result: Option[T]): Result[T] = {
        result match {
            case Some(value) ⇒ Success(value)
            case _ /*None*/  ⇒ Failure
        }
    }
}

/**
 * The computation '''succeeded''' and produced a result. In general
 */
case class Success[@specialized(Int) +T](value: T) extends Result[T] {

    class FilteredSuccess(p: (T) ⇒ Boolean) extends Result[T] {
        def hasValue: Boolean = p(value)
        def value: T = {
            if (hasValue) {
                value
            } else {
                throw new UnsupportedOperationException("the result is filtered")
            }
        }
        def foreach[U](f: (T) ⇒ U): Unit = if (p(value)) f(value)
        def map[B](f: (T) ⇒ B): Result[B] = if (p(value)) Success(f(value)) else Empty
        def flatMap[B](f: (T) ⇒ Result[B]): Result[B] = if (p(value)) f(value) else Empty
        def withFilter(p: (T) ⇒ Boolean): Result[T] = new FilteredSuccess((t: T) ⇒ p(t) && this.p(t))
        def toSet[X >: T]: Set[X] = if (p(value)) Set(value) else Set.empty
        def toOption: Option[T] = if (p(value)) Some(value) else None
    }

    def hasValue: Boolean = true
    def foreach[U](f: (T) ⇒ U): Unit = f(value)
    def map[B](f: (T) ⇒ B): Success[B] = Success(f(value))
    def flatMap[B](f: (T) ⇒ Result[B]): Result[B] = f(value)
    def withFilter(p: (T) ⇒ Boolean): Result[T] = new FilteredSuccess(p)
    def toSet[X >: T]: Set[X] = Set(value)
    def toOption: Option[T] = Some(value)
}

sealed trait NoResult extends Result[Nothing] {
    final def hasValue: Boolean = false
    final def value: Nothing = throw new UnsupportedOperationException("this result has no value")
    final def foreach[U](f: (Nothing) ⇒ U): Unit = {}
    final def map[B](f: (Nothing) ⇒ B): this.type = this
    final def flatMap[B](f: (Nothing) ⇒ Result[B]): this.type = this
    final def withFilter(q: (Nothing) ⇒ Boolean): this.type = this
    final def toSet[X >: Nothing]: Set[X] = Set.empty
    final def toOption: Option[Nothing] = None
}

object NoResult {
    def unapply(result: Result[_]): Boolean = !result.hasValue
}

/**
 * The computation '''finished''', but did no produce any results or the result was filtered.
 *
 * @note    The precise semantics of ''succeeded without results'' is dependent on the semantics
 *          of the concrete computation and needs to be defined per use case.
 */
case object Empty extends NoResult

/**
 * The computation '''failed''' because of missing/incomplete information.
 *
 * @note    The precise semantics of the ''computation failed'' is dependent on the semantics
 *          of the concrete computation and needs to be defined per use case.
 */
case object Failure extends NoResult




© 2015 - 2025 Weber Informatics LLC | Privacy Policy