def tuple[A, B, C, D]()

in src/main/scala/com/twitter/stitch/Arrow.scala [3689:3879]


  def tuple[A, B, C, D](a1: Arrow[A, B], a2: Arrow[C, D]): Arrow[(A, C), (B, D)] =
    join(a1.contramap(_._1), a2.contramap[(A, C)](_._2))

  /**
   * `Arrow.zipWithArg` is equivalent to `{ x => a(x).map((x, _)) }`
   */
  def zipWithArg[T, U](f: Arrow[T, U]): Arrow[T, (T, U)] =
    Arrow.join(Arrow.identity[T], f)

  /**
   * let-scope a [[TwitterLocal]] around the contained [[Arrow]] `a` based on a by-name `local`
   * When running this [[Arrow]], `local` is evaluated once per call to [[Arrow.run]], meaning it's evaluated only once for each batch.
   *
   * This is equivalent to `local.let(local)(a)` but correctly applies the `local` to the `a`'s execution.
   *
   * @note [[let]] applies the [[Local]] __only non-batched operations__,
   *       [[call]] and [[Group]]s do not have access to [[Local]]s set with [[let]].
   *       To access a [[Local]] value within a [[call]], or [[Group]], access the value
   *       before making your [[call]] and add it to your input.
   *       For instance,`a.map(v => (v, local())).call(...)`
   *
   * @param local the [[TwitterLocal]] that should be let-scoped around `a`
   * @param value the value of the `local` within the execution of `a`
   * @param a the underlying [[Arrow]] which will have the `local` set during it's evaluation
   * @tparam L the type of the [[TwitterLocal]] and it's value
   */
  def let[T, U, L](local: TwitterLocal[L])(value: => L)(a: Arrow[T, U]): Arrow[T, U] =
    let(LocalLetter(local))(value)(a)

  /**
   * let-scope a [[TwitterLocal]] around the contained [[Arrow]] `a` based on the input
   *
   * This is equivalent to `local.let(local)(a)` but correctly applies the `local` to the `a`'s execution.
   *
   * If your `update` returns a constant value, [[let]] is a more efficient.
   *
   * @note [[letWithArg]] applies the [[Local]] __only non-batched operations__,
   *       [[call]] and [[Group]]s do not have access to [[Local]]s set with [[letWithArg]].
   *       To access a [[Local]] value within a [[call]], or [[Group]], access the value
   *       before making your [[call]] and add it to your input.
   *       For instance,`a.map(v => (v, local())).call(...)`
   *
   * @note if the input to this [[Arrow]] is in a [[Throw]] state then the underlying [[TwitterLocal]] will not be scoped around `a`
   *       but `a` will be executed with the underlying [[Throw]] passed in.
   *       Similarly, if `update` throws, then `a` will be executed with a [[Throw]] of that exception without the
   *       [[TwitterLocal]] being scoped.
   *
   * @param local the [[TwitterLocal]] that should be let-scoped around `a`
   * @param update a function which will be evaluated to determine the value of the `local` within the execution of `a`
   * @param a the underlying [[Arrow]] which will have the `local` set during it's evaluation
   * @tparam L the type of the [[TwitterLocal]] and it's value
   */
  def letWithArg[T, U, L](local: TwitterLocal[L])(update: T => L)(a: Arrow[T, U]): Arrow[T, U] =
    letWithArg(LocalLetter(local))(update)(a)

  /**
   * clear a [[TwitterLocal]] around the contained [[Arrow]] `a`
   *
   * This is equivalent to `local.letClear(a)` but correctly clears the `local` for `a`'s execution.
   *
   * @param local the [[TwitterLocal]] that should be let-scoped around `a`
   * @param a the underlying [[Arrow]] which will have the `local` set during it's evaluation
   */
  def letClear[T, U](local: TwitterLocal[_])(a: => Arrow[T, U]): Arrow[T, U] =
    letClear(LocalLetClearer(local))(a)

  /**
   * let-scope a [[TwitterLocal]] around the contained [[Arrow]] `a` based on a by-name `value`
   * When running this [[Arrow]], `local` is evaluated once per call to [[Arrow.run]], meaning it's evaluated only once for each batch.
   *
   * This is equivalent to `Letter.let(value)(a)` but correctly applies the [[Letter.let]] to the `a`'s execution.
   *
   * This is used for let-scoping [[TwitterLocal]]s which are private to a class and can only be accessed with their own
   * let method instead of giving access to the underlying [[TwitterLocal]]
   *
   * @note [[let]] applies the [[Local]] __only non-batched operations__,
   *       [[call]] and [[Group]]s do not have access to [[Local]]s set with [[let]].
   *       To access a [[Local]] value within a [[call]], or [[Group]], access the value
   *       before making your [[call]] and add it to your input.
   *       For instance,`a.map(v => (v, local())).call(...)`
   *
   * @param letter [[Letter]] that let-scopes around a block of code
   * @param a the underlying [[Arrow]] which will have the `local` set during it's evaluation
   * @tparam L the type of the [[TwitterLocal]] and it's value
   */
  def let[T, U, L](letter: Letter[L])(value: => L)(a: Arrow[T, U]): Arrow[T, U] =
    TwitterLocals.Let(() => value, letter, a)

  /**
   * let-scope a [[TwitterLocal]] around the contained [[Arrow]] `a` based on the input
   *
   * This is equivalent to `Letter.let(value)(a)` where `value` changes for each argument in the batch
   * but correctly applies the [[Letter.let]] to the `a`'s execution.
   *
   * If your `update` returns a constant value, [[let]] is a more efficient.
   *
   * This is used for let-scoping [[TwitterLocal]]s which are private to a class and can only be accessed with their own
   * let method instead of giving access to the underlying [[TwitterLocal]]
   *
   * @note [[letWithArg]] applies the [[Local]] __only non-batched operations__,
   *       [[call]] and [[Group]]s do not have access to [[Local]]s set with [[letWithArg]].
   *       To access a [[Local]] value within a [[call]], or [[Group]], access the value
   *       before making your [[call]] and add it to your input.
   *       For instance,`a.map(v => (v, local())).call(...)`
   *
   * @note if the input to this [[Arrow]] is in a [[Throw]] state then the underlying [[TwitterLocal]] will not be scoped around `a`
   *       but `a` will be executed with the underlying [[Throw]] passed in.
   *       Similarly, if `update` throws, then `a` will be executed with a [[Throw]] of that exception without the
   *       [[TwitterLocal]] being scoped.
   *
   * @param letter [[Letter]] that let-scopes around a block of code
   * @param update a function which will be evaluated to determine the value of the `local` within the execution of `a`
   * @param a the underlying [[Arrow]] which will have the `local` set during it's evaluation
   * @tparam L the type of the [[TwitterLocal]] and it's value
   */
  def letWithArg[T, U, L](letter: Letter[L])(update: T => L)(a: Arrow[T, U]): Arrow[T, U] =
    TwitterLocals.LetWithArg[T, U, L](letter, update, a)

  /**
   * clear a [[TwitterLocal]] around the contained [[Arrow]] `a`
   *
   * This is equivalent to `LetClearer.letClear(a)` but correctly clears the `local` for `a`'s execution.
   *
   * This is used for let-scoping [[TwitterLocal]]s which are private to a class and can only be accessed with their own
   * let method instead of giving access to the underlying [[TwitterLocal]]
   *
   * @param letClearer the [[TwitterLocal]] that should be let-scoped around `a`
   * @param a the underlying [[Arrow]] which will have the `local` set during it's evaluation
   */
  def letClear[T, U](letClearer: LetClearer)(a: Arrow[T, U]): Arrow[T, U] =
    TwitterLocals.LetClear(letClearer, a)

  /**
   * Creates a new `Arrow[T, U]` that dispatches input to one of two sub-arrows
   * based on the result of a predicate.  This is equivalent to
   * `{ x => if (cond(x)) a1(x) else a2(x) }`.
   */
  def ifelse[T, U](cond: T => Boolean, a1: Arrow[T, U], a2: Arrow[T, U]): Arrow[T, U] =
    choose[T, U](
      (cond, a1),
      (_ => true, a2)
    )

  /**
   * Creates a new `Arrow[T, U]` that dispatches input to the first
   * sub-arrow for which the corresponding predicate returns true.  If
   * no predicate returns true for the input, that will result in a
   * `MatchError` for that input.
   *
   * Behavior is undefined if a predicate throws an exception.
   */
  def choose[T, U](choices: Choice[T, U]*): Arrow[T, U] =
    Choose(choices)

  /**
   * Encapsulates a choice for the `choose` method.  If the given predicate returns true
   * for a given input, then the corresponding arrow is invoked for that input.
   */
  type Choice[-T, +U] = (T => Boolean, Arrow[T, U])

  object Choice {

    /**
     * Builds a `Choice` that dispatches to the given `Arrow` when the given predicate is
     * true for the input.
     */
    def when[T, U](cond: T => Boolean, arrow: Arrow[T, U]): Choice[T, U] =
      (cond, arrow)

    /**
     * Builds a `Choice` that dispatches to the given `Arrow` when the given `PartialFunction`
     * is defined at the input.
     */
    def ifDefinedAt[T, S, U](pf: PartialFunction[T, S], a: Arrow[S, U]): Choice[T, U] =
      // we make this transformation to expose constant arrows so
      // Choose can optimize them. Ordinarily it's not valid to
      // transform Arrow.map(f).andThen(Arrow.Const(t)) to
      // Arrow.Const(t), because f may throw an exception; but here
      // pf may not throw an exception.
      a match {
        case Const(_) => (pf.isDefinedAt, a.asInstanceOf[Arrow[T, U]])
        case _ => (pf.isDefinedAt, Arrow.map(pf).andThen(a))
      }

    /**
     * Buildes a `Choice` with a predicate that is always `true`.  This can be used
     * as the last `Choice` as a catch-all.
     */
    def otherwise[T, U](a: Arrow[T, U]): Choice[T, U] =
      (_ => true, a)
  }