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)
}
}
}