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

com.twitter.util.Try.scala Maven / Gradle / Ivy

The newest version!
package com.twitter.util

/**
 * The Try type represents a computation that may either result in an exception
 * or return a success value. It is analogous to the Either type but encodes
 * common idioms for handling exceptional cases (such as rescue/ensure which
 * is analogous to try/finally).
 */
object Try {
  case class PredicateDoesNotObtain() extends Exception()

  def apply[R](r: => R): Try[R] = {
    try { Return(r) } catch {
      case NonFatal(e) => Throw(e)
    }
  }

  /**
   * Collect the results from the given Trys into a new Try. The result will be a Throw if any of
   * the argument Trys are Throws. The first Throw in the Seq is the one which is surfaced.
   */
  def collect[A](ts: Seq[Try[A]]): Try[Seq[A]] = {
    if (ts.isEmpty) Return(Seq.empty[A]) else Try { ts map { t => t() } }
  }
}

/**
 * This class represents a computation that can succeed or fail. It has two
 * concrete implementations, Return (for success) and Throw (for failure)
 */
sealed abstract class Try[+R] {
  /**
   * Returns true if the Try is a Throw, false otherwise.
   */
  def isThrow: Boolean

  /**
   * Returns true if the Try is a Return, false otherwise.
   */
  def isReturn: Boolean

  /**
   * Returns the value from this Return or the given argument if this is a Throw.
   */
  def getOrElse[R2 >: R](default: => R2) = if (isReturn) apply() else default

  /**
   * Returns the value from this Return or throws the exception if this is a Throw
   */
  def apply(): R

  /**
   * Returns the value from this Return or throws the exception if this is a Throw.
   * Alias for apply()
   */
  def get() = apply()

  /**
   * Applies the given function f if this is a Result.
   */
  def foreach(f: R => Unit) { onSuccess(f) }

  /**
   * Returns the given function applied to the value from this Return or returns this if this is a Throw.
   *
   * ''Note'' The gnarly type parameterization is there for Java compatibility, since Java
   * does not support higher-kinded types.
   */
  def flatMap[R2](f: R => Try[R2]): Try[R2]

  /**
   * Maps the given function to the value from this Return or returns this if this is a Throw
   */
  def map[X](f: R => X): Try[X]

  /**
   * Returns true if this Try is a Return and the predicate p returns true when
   * applied to its value.
   */
  def exists(p: R => Boolean): Boolean

  /**
   * Converts this to a Throw if the predicate does not obtain.
   */
  def filter(p: R => Boolean): Try[R]

  /**
   * Converts this to a Throw if the predicate does not obtain.
   */
  def withFilter(p: R => Boolean): Try[R]

  /**
   * Calls the exceptionHandler with the exception if this is a Throw. This is like flatMap for the exception.
   *
   * ''Note'' The gnarly type parameterization is there for Java compatibility, since Java
   * does not support higher-kinded types.
   */
  def rescue[R2 >: R](rescueException: PartialFunction[Throwable, Try[R2]]): Try[R2]

  /**
   * Calls the exceptionHandler with the exception if this is a Throw. This is like map for the exception.
   */
  def handle[R2 >: R](rescueException: PartialFunction[Throwable, R2]): Try[R2]

  /**
   * Invoked only if the computation was successful.  Returns a
   * chained `this` as in `respond`.
   */
  def onSuccess(f: R => Unit): Try[R]

  /**
   * Invoke the function on the error, if the computation was
   * unsuccessful.  Returns a chained `this` as in `respond`.
   */
  def onFailure(rescueException: Throwable => Unit): Try[R]

  /**
   * Invoked regardless of whether the computation completed
   * successfully or unsuccessfully.  Implemented in terms of
   * `respond` so that subclasses control evaluation order.  Returns a
   * chained `this` as in `respond`.
   */
  def ensure(f: => Unit): Try[R] =
    respond { _ => f }

  /**
   * Returns None if this is a Throw or a Some containing the value if this is a Return
   */
  def toOption = if (isReturn) Some(apply()) else None

  /**
   * Invokes the given closure when the value is available.  Returns
   * another 'This[R]' that is guaranteed to be available only *after*
   * 'k' has run.  This enables the enforcement of invocation ordering.
   */
  def respond(k: Try[R] => Unit): Try[R] = { k(this); this }

  /**
   * Invokes the given transformation when the value is available,
   * returning the transformed value. This method is like a combination
   * of flatMap and rescue. This method is typically used for more
   * imperative control-flow than flatMap/rescue which often exploits
   * the Null Object Pattern.
   */
  def transform[R2](f: Try[R] => Try[R2]): Try[R2] = f(this)

  /**
   * Returns the given function applied to the value from this Return or returns this if this is a Throw.
   * Alias for flatMap
   */
  def andThen[R2](f: R => Try[R2]) = flatMap(f)

  def flatten[T](implicit ev: R <:< Try[T]): Try[T]
}

final case class Throw[+R](e: Throwable) extends Try[R] {
  def isThrow = true
  def isReturn = false
  def rescue[R2 >: R](rescueException: PartialFunction[Throwable, Try[R2]]) = {
    try {
      if (rescueException.isDefinedAt(e)) rescueException(e) else this
    } catch {
      case NonFatal(e2) => Throw(e2)
    }
  }
  def apply(): R = throw e
  def flatMap[R2](f: R => Try[R2]) = this.asInstanceOf[Throw[R2]]
  def flatten[T](implicit ev: R <:< Try[T]): Try[T] = this.asInstanceOf[Throw[T]]
  def map[X](f: R => X) = this.asInstanceOf[Throw[X]]
  def exists(p: R => Boolean) = false
  def filter(p: R => Boolean) = this
  def withFilter(p: R => Boolean) = this
  def onFailure(rescueException: Throwable => Unit) = { rescueException(e); this }
  def onSuccess(f: R => Unit) = this
  def handle[R2 >: R](rescueException: PartialFunction[Throwable, R2]) =
    if (rescueException.isDefinedAt(e)) {
      Try(rescueException(e))
    } else {
      this
    }
}

object Return {
  val Unit = Return(())
  val Void = Return[Void](null)
  val None: Return[Option[Nothing]] = Return(Option.empty)
  val Nil: Return[Seq[Nothing]] = Return(Seq.empty)
  val True: Return[Boolean] = Return(true)
  val False: Return[Boolean] = Return(false)
}

final case class Return[+R](r: R) extends Try[R] {
  def isThrow = false
  def isReturn = true
  def rescue[R2 >: R](rescueException: PartialFunction[Throwable, Try[R2]]) = Return(r)
  def apply() = r
  def flatMap[R2](f: R => Try[R2]) = try f(r) catch { case NonFatal(e) => Throw(e) }
  def flatten[T](implicit ev: R <:< Try[T]): Try[T] = r
  def map[X](f: R => X) = Try[X](f(r))
  def exists(p: R => Boolean) = p(r)
  def filter(p: R => Boolean) = if (p(apply())) this else Throw(new Try.PredicateDoesNotObtain)
  def withFilter(p: R => Boolean) = filter(p)
  def onFailure(rescueException: Throwable => Unit) = this
  def onSuccess(f: R => Unit) = { f(r); this }
  def handle[R2 >: R](rescueException: PartialFunction[Throwable, R2]) = this
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy