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

org.codeswarm.fallible.Fallible.scala Maven / Gradle / Ivy

The newest version!
package org.codeswarm.fallible

/** Represents a value or a list of errors explaining why the value is absent.
  * Instances of `Fallible[A, E]` are an instance of either `Success[A]` or `Failure[E]`.
  */
sealed abstract class Fallible[+A, +E] {

  /** Returns true if this is an instance of `Success`, or false otherwise.
    */
  def isSuccess: Boolean =
    this match {
      case Success(_) => true
      case Failure(_) => false
    }

  /** Returns true if this is an instance of `Failure`, or false otherwise.
    */
  def isFailure: Boolean = !isSuccess

  /** Returns `Some` if this is an instance of `Success`, or `None` otherwise.
    */
  def toOption: Option[A] =
    this match {
      case Success(x) => Some(x)
      case Failure(_) => None
    }

  /** Returns all errors.
    *
    * If this is an instance of `Success`, returns an empty list, An instance of
    * `Failure` may also return an empty list, denoting failure without any explanation.
    */
  def getErrors: List[E] =
    this match {
      case Success(_) => List()
      case Failure(e) => e
    }

  /** Returns `Right` if this is an instance of `Success`, or `Left` otherwise.
    */
  def toEither: Either[List[E], A] =
    this match {
      case Success(x) => Right(x)
      case Failure(e) => Left(e)
    }

  /** Returns the result of applying `f` to this object's value if this is an instance of `Success`.
    */
  def map[B, E1 >: E](f: A => B): Fallible[B, E1] =
    this match {
      case Success(x) => Success(f(x))
      case Failure(e) => Failure(e)
    }

  /** Returns the result of applying a fallible transformation `f` to this
    * object's value if this is an instance of success.
    *
    * The resulting object will be an instance of `Success` if and only if
    * both this and the transformation result are instances of `Success`.
    */
  def flatMap[B, E1 >: E](f: A => Fallible[B, E1]): Fallible[B, E1] =
    this match {
      case Success(x) => f(x)
      case Failure(e) => Failure(e)
    }

  /** Returns the result of applying `f` to each error.
    */
  def mapError[E1](f: E => E1): Fallible[A, E1] =
    this match {
      case Success(x) => Success(x)
      case Failure(e) => Failure(e map f)
    }

  /** Returns the first `Success` among `this` and `that`, or `Failure` if both are failures.
    *
    * This is somewhat analogous to a Boolean '''or''' operation, because the result
    * is `Success` if and only if `this` '''or''' `that` is `Success`.
    *
    * If both `this` and `that` are instances of `Failure`, then the result contains
    * the errors from both `Failures`.
    *
    * This method may be useful if you need to select a single value
    * from a sequence of fallible fallbacks.
    */
  def || [A1 >: A, E1 >: E](that: Fallible[A1, E1]): Fallible[A1, E1] =
    ( this, that ) match {
      case ( Success(x), _ ) => Success(x)
      case ( Failure(_), Success(x) ) => Success(x)
      case ( Failure(e1), Failure(e2) ) => Failure(e1 ++ e2)
    }

  /** Returns `Success` containing a tuple of `this` value and `that` value if '''both'''
    * `this` and `that` are instances of `Success`, or `Failure` if either is an instance of `Failure`.
    *
    * This is somewhat analogous to a Boolean '''and''' operation, because the result is `Success`
    * if and only if `this` '''and''' `that` are `Success`.
    */
  def && [B, E1 >: E](that: Fallible[B, E1]): Fallible[(A, B), E1] =
    ( this, that ) match {
      case ( Success(x1), Success(x2) ) => Success((x1, x2))
      case ( a, b ) => Failure(List(a, b).map(_.getErrors).flatten)
    }

}

object Fallible {

  /** Constructs a `Fallable` from an `Option` by mapping
    * `Some(x)` to `Success(x)` and `None` to `Failure`.
    * If the result is `Failure`, its list of errors is empty.
    */
  def apply[A](o: Option[A]): Fallible[A, Nothing] =
    o match {
      case Some(x) => Success(x)
      case None => Failure()
    }

  /** Constructs a `Fallable` from an `Option` by mapping
    * `Some(x)` to `Success(x)` and `None` to `Failure`.
    * If the result is `Failure`, it contains a single error provided by `e`.
    */
  def apply[A, E](o: Option[A], e: => E): Fallible[A, E] =
    o match {
      case Some(x) => Success(x)
      case None => Failure(e)
    }

}

/** Class `Success[A]` represents existing values of type `A`.
  */
case class Success[+A](value: A) extends Fallible[A, Nothing]

/** Class `Failure[E]` represents a list of errors of type `E`.
  */
case class Failure[+E](errors: List[E]) extends Fallible[Nothing, E]

object Failure {

  def apply[A, E](errors: E*): Failure[E] = Failure(errors.toList)

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy