def handle[T, U >: T]()

in src/main/scala/com/twitter/stitch/Arrow.scala [2448:2628]


  def handle[T, U >: T](f: PartialFunction[Throwable, U]): Arrow[T, U] = Handle(f)

  /**
   * `Arrow.rescue(f)` is equivalent to `Arrow.identity.rescue(f)`
   */
  def rescue[T, U >: T](f: PartialFunction[Throwable, Stitch[U]]): Arrow[T, U] = Rescue(f)

  /**
   * `Arrow.mapFailure(pf)` is equivalent to `Arrow.identity.mapFailure(pf)`
   */
  def mapFailure[T](pf: PartialFunction[Throwable, Throwable]): Arrow.Iso[T] = MapFailure(pf)

  /**
   * `Arrow.ensure(f)` is equivalent to `Arrow.identity.ensure(f)`
   */
  def ensure[T](f: => Unit): Arrow.Iso[T] = Ensure(() => f)

  /**
   * `Arrow.respond(f)` is equivalent to `Arrow.identity.respond(f)`
   */
  def respond[T](f: Try[T] => Unit): Arrow.Iso[T] = Respond(f)

  /**
   * `Arrow.onSuccess(f)` is equivalent to `Arrow.identity.onSuccess(f)`
   */
  def onSuccess[T](f: T => Unit): Arrow.Iso[T] = OnSuccess(f)

  /**
   * `Arrow.onFailure(f)` is equivalent to `Arrow.identity.onFailure(f)`
   */
  def onFailure[T](f: Throwable => Unit): Arrow.Iso[T] = OnFailure(f)

  /**
   * `Arrow.transform(f)` is equivalent to `Arrow.identity.transform(f)`
   */
  def transform[T, U](f: Try[T] => Stitch[U]): Arrow[T, U] = FlatmapTry(f)

  /**
   * `Arrow.transformTry(f)` is equivalent to `Arrow.identity.liftToTry.map(f).lowerFromTry`
   */
  def transformTry[T, U](f: Try[T] => Try[U]): Arrow[T, U] =
    TransformTry(f, mayHaveEffect = true, mayHandleExceptions = true)

  /**
   * `Arrow.unit` is equivalent to `Arrow.identity.unit`
   */
  def unit[T]: Arrow[T, Unit] = ToUnit

  /**
   * `Arrow.liftToTry` is equivalent to `Arrow.identity.liftToTry`
   */
  def liftToTry[T]: Arrow[T, Try[T]] = liftToTryInstance.asInstanceOf[LiftToTry[T]]

  /**
   * `Arrow.lowerFromTry` is equivalent to `Arrow.identity.lowerFromTry`
   */
  def lowerFromTry[T]: Arrow[Try[T], T] = lowerFromTryInstance.asInstanceOf[LowerFromTry[T]]

  /**
   * `Arrow.liftToOption(f)` is equivalent to `Arrow.identity.liftToOption(f)`
   */
  def liftToOption[T](
    f: PartialFunction[Throwable, Boolean] = Stitch.liftToOptionDefaultFn
  ): Arrow[T, Option[T]] =
    LiftToOption(f)

  /**
   * `Arrow.liftNotFoundToOption` is equivalent to `Arrow.identity.liftNotFoundToOption`
   */
  def liftNotFoundToOption[T]: Arrow[T, Option[T]] =
    LiftToOption(Stitch.liftNotFoundToOptionFn)

  /**
   * `Arrow.lowerFromOption(e)` is equivalent to `Arrow.identity.lowerFromOption(e)`
   */
  def lowerFromOption[T](e: Throwable = com.twitter.stitch.NotFound): Arrow[Option[T], T] =
    LowerFromOption(e)

  /**
   * `Arrow.time(a)` is equivalent to `Arrow { x => Stitch.time(a(x)) }`
   */
  def time[T, U](a: Arrow[T, U]): Arrow[T, (Try[U], Duration)] = Time(a)

  /**
   * `Arrow.within(dur)(a)` is equivalent to `Arrow { x => a(x).within(dur) }`
   */
  def within[T, U](timeout: Duration)(a: Arrow[T, U])(implicit timer: Timer): Arrow[T, U] =
    Within(timeout, timer, a)

  private[stitch] object AndThen {

    /**
     * reassoc is used when building arrows which will be persistent and used multiple times
     * this will recursively optimize the form of the arrow so that it will run more efficiently at the expense
     * of more work during creation time
     */
    def reassoc[T, U, V](f: Arrow[T, U], g: Arrow[U, V]): Arrow[T, V] =
      f match {
        case Identity() => g.asInstanceOf[Arrow[T, V]]

        // arrows are built up by left-associated calls to andThen (e.g. in a.map(f).map(g)),
        // but we want them to be right-associated so that when we consume then // we don't
        // need to reassociate to find the head, so we reassociate here.
        case AndThen(d, e: Arrow[Any, U] @unchecked) => d.andThen(e.andThen(g))

        // We can execute some computations on Const arrows at composition
        // time (constant folding.)
        //
        // It's not safe to do this for arrows that call user-defined code,
        // since that code may have effects, and we want to make sure that
        // those effects are not optimized away. For instance, the code may
        // increment a counter, or access the system clock:
        //
        // >>> val count = Arrow.map[Any, Unit](_ => incrCounter())
        // >>> val currentTime = Arrow.map[Any, Time](_ => Time.now)
        //
        // We also cannot constant-fold for operations that return different
        // results for different `Throw` inputs, since the exception may be
        // different at evaluation time then at constant-folding time. For
        // instance, consider these two expressions:
        //
        // object E1 extends Exception
        // object E2 extends Exception
        // >>> val arr = Arrow.exception(E2).andThen(Arrow.liftToTry)
        // >>> val arr2 = Arrow.exception(E1).andThen(arr)
        //
        // `arr` for any input should yield Return(Throw(E2)), and `arr2`
        // should yield Return(Throw(E1)), but if `arr` was constant-folded,
        // `arr2` would instead yield Throw(E1) (that is, the liftToTry
        // would not happen.)
        case Const(t) =>
          g match {
            case g: PureArrow[U @unchecked, V @unchecked]
                if !g.mayHaveEffect && !g.mayHandleExceptions =>
              Const(g.applyPure(t))

            case AndThen(g: PureArrow[U @unchecked, Any @unchecked], h: Arrow[Any, V] @unchecked)
                if !g.mayHaveEffect && !g.mayHandleExceptions =>
              Const(g.applyPure(t)).andThen(h)

            // We don't need to handle AndThen(AndThen(a, b), c) because
            // that's eliminated by the reassociation above.
            case _ =>
              AndThen(f, g)
          }

        // with the batch interface for arrows we iterate over all input arguments for each arrow
        // this is unnecessary in the case for pure arrows that are andThen'ed together.
        // Here we take the case of pure arrows that are andThen'ed and apply the
        // pure transformation inline which reduces iterations over the input when
        // working in batches
        case f: PureArrow[T, U]
            if g.isInstanceOf[PureArrow[U, V]] && !g.isInstanceOf[Identity[_]] =>
          val gg = g.asInstanceOf[PureArrow[U, V]]
          TransformTry[T, V](
            (t: Try[T]) => gg.applyPure(f.applyPure(t)),
            f.mayHaveEffect || gg.mayHaveEffect,
            f.mayHandleExceptions || gg.mayHandleExceptions
          )

        case _ =>
          g match {
            case Identity() => f.asInstanceOf[Arrow[T, V]]
            case _ => AndThen(f, g)
          }
      }

    /**
     * noReassoc is used when building arrows which are not going to be reused,
     * where the cost of recursive build time optimization is not worth it for a single execution
     */
    def noReassoc[T, U, V](f: Arrow[T, U], g: Arrow[U, V]): Arrow[T, V] =
      f match {
        case Identity() => g.asInstanceOf[Arrow[T, V]]
        case _ =>
          g match {
            case Identity() => f.asInstanceOf[Arrow[T, V]]
            case _ => AndThen(f, g)
          }
      }
  }