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

shapeless.ops.tuples.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2013-15 Miles Sabin
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package shapeless
package ops

import scala.annotation.implicitNotFound

object tuple {
  import shapeless.ops.{ hlist => hl }

  /**
   * Type class witnessing that this tuple is composite and providing access to head and tail.
   *
   * @author Miles Sabin
   */
  trait IsComposite[P] extends Serializable {
    type H
    type T

    def head(p : P) : H
    def tail(p : P) : T
  }

  object IsComposite {
    def apply[P](implicit isComp: IsComposite[P]): Aux[P, isComp.H, isComp.T] = isComp

    type Aux[P, H0, T0] = IsComposite[P] { type H = H0; type T = T0 }

    implicit def isComposite[P, L <: HList, H0, T <: HList]
      (implicit gen: Generic.Aux[P, L], isHCons: hl.IsHCons.Aux[L, H0, T], tp: hl.Tupler[T]): Aux[P, H0, tp.Out] =
        new IsComposite[P] {
          type H = H0
          type T = tp.Out
          def head(p: P): H = isHCons.head(gen.to(p))
          def tail(p: P): T = tp(isHCons.tail(gen.to(p)))
        }
  }

  /**
   * Type class supporting prepending to this tuple.
   *
   * @author Miles Sabin
   */
  trait Prepend[T, U] extends DepFn2[T, U] with Serializable

  object Prepend {
    def apply[T, U](implicit prepend: Prepend[T, U]): Aux[T, U, prepend.Out] = prepend

    type Aux[T, U, Out0] = Prepend[T, U] { type Out = Out0 }

    implicit def prepend[T, L1 <: HList, U, L2 <: HList, L3 <: HList]
      (implicit gent: Generic.Aux[T, L1], genu: Generic.Aux[U, L2], prepend: hl.Prepend.Aux[L1, L2, L3], tp: hl.Tupler[L3]): Aux[T, U, tp.Out] =
        new Prepend[T, U] {
          type Out = tp.Out
          def apply(t: T, u: U): Out = prepend(gent.to(t), genu.to(u)).tupled
        }
  }

  /**
   * Type class supporting reverse prepending to this tuple.
   *
   * @author Miles Sabin
   */
  trait ReversePrepend[T, U] extends DepFn2[T, U] with Serializable

  object ReversePrepend {
    def apply[T, U](implicit prepend: ReversePrepend[T, U]): Aux[T, U, prepend.Out] = prepend

    type Aux[T, U, Out0] = ReversePrepend[T, U] { type Out = Out0 }

    implicit def prepend[T, L1 <: HList, U, L2 <: HList, L3 <: HList]
      (implicit gent: Generic.Aux[T, L1], genu: Generic.Aux[U, L2], prepend: hl.ReversePrepend.Aux[L1, L2, L3], tp: hl.Tupler[L3]): Aux[T, U, tp.Out] =
        new ReversePrepend[T, U] {
          type Out = tp.Out
          def apply(t: T, u: U): Out = prepend(gent.to(t), genu.to(u)).tupled
        }
  }

  /**
   * Type class supporting access to the ''nth'' element of this tuple. Available only if this tuple has at least
   * ''n'' elements.
   *
   * @author Miles Sabin
   */
  trait At[T, N <: Nat] extends DepFn1[T] with Serializable

  object At {
    def apply[T, N <: Nat](implicit at: At[T, N]): Aux[T, N, at.Out] = at

    type Aux[T, N <: Nat, Out0] = At[T, N] { type Out = Out0 }

    implicit def at[T, L1 <: HList, N <: Nat]
      (implicit gen: Generic.Aux[T, L1], at: hl.At[L1, N]): Aux[T, N, at.Out] =
        new At[T, N] {
          type Out = at.Out
          def apply(t: T): Out = at(gen.to(t))
        }
  }

  /**
   * Type class supporting access to the last element of this tuple. Available only if this tuple has at least one
   * element.
   *
   * @author Miles Sabin
   */
  trait Last[T] extends DepFn1[T] with Serializable

  object Last {
    def apply[T](implicit last: Last[T]): Aux[T, last.Out] = last

    type Aux[T, Out0] = Last[T] { type Out = Out0 }

    implicit def last[T, L <: HList]
      (implicit gen: Generic.Aux[T, L], last: hl.Last[L]): Aux[T, last.Out] =
        new Last[T] {
          type Out = last.Out
          def apply(t: T): Out = gen.to(t).last
        }
  }

  /**
   * Type class supporting access to all but the last element of this tuple. Available only if this tuple has at
   * least one element.
   *
   * @author Miles Sabin
   */
  trait Init[T] extends DepFn1[T] with Serializable

  object Init {
    def apply[T](implicit init: Init[T]): Aux[T, init.Out] = init

    type Aux[T, Out0] = Init[T] { type Out = Out0 }

    implicit def init[T, L1 <: HList, L2 <: HList]
      (implicit gen: Generic.Aux[T, L1], init: hl.Init.Aux[L1, L2], tp: hl.Tupler[L2]): Aux[T, tp.Out] =
        new Init[T] {
          type Out = tp.Out
          def apply(t: T): Out = init(gen.to(t)).tupled
        }
  }

  /**
   * Type class supporting access to the first element of this tuple of type `U`. Available only if this tuple
   * contains an element of type `U`.
   *
   * @author Miles Sabin
   */
  trait Selector[T, U] extends DepFn1[T] with Serializable { type Out = U }

  object Selector {
    def apply[T, U](implicit selector: Selector[T, U]): Aux[T, U] = selector

    type Aux[T, U] = Selector[T, U]

    implicit def select[T, L <: HList, U]
      (implicit gen: Generic.Aux[T, L], selector: hl.Selector[L, U]): Aux[T, U] =
        new Selector[T, U] {
          def apply(t: T): U = gen.to(t).select[U]
        }
  }

  /**
   * Type class supporting access to the all elements of this tuple of type `U`.
   *
   * @author Miles Sabin
   */
  trait Filter[T, U] extends DepFn1[T] with Serializable

  object Filter {
    def apply[T, U](implicit filter: Filter[T, U]): Aux[T, U, filter.Out] = filter

    type Aux[T, U, Out0] = Filter[T, U] { type Out = Out0 }

    implicit def filterTuple[T, L1 <: HList, U, L2 <: HList]
      (implicit gen: Generic.Aux[T, L1], filter: hl.Filter.Aux[L1, U, L2], tp: hl.Tupler[L2]): Aux[T, U, tp.Out] = new Filter[T, U] {
        type Out = tp.Out
        def apply(t: T): Out = tp(filter(gen.to(t)))
      }
  }

  /**
   * Type class supporting access to the all elements of this tuple of type different than `U`.
   *
   * @author Miles Sabin
   */
  trait FilterNot[T, U] extends DepFn1[T] with Serializable

  object FilterNot {
    def apply[T, U](implicit filter: FilterNot[T, U]): Aux[T, U, filter.Out] = filter

    type Aux[T, U, Out0] = FilterNot[T, U] { type Out = Out0 }

    implicit def filterNotTuple[T, L1 <: HList, U, L2 <: HList]
      (implicit gen: Generic.Aux[T, L1], filterNot: hl.FilterNot.Aux[L1, U, L2], tp: hl.Tupler[L2]): Aux[T, U, tp.Out] = new FilterNot[T, U] {
        type Out = tp.Out
        def apply(t: T): Out = tp(filterNot(gen.to(t)))
      }
  }

  /**
   * Type class supporting removal of an element from this tuple. Available only if this tuple contains an
   * element of type `U`.
   *
   * @author Miles Sabin
   */
  trait Remove[T, U] extends DepFn1[T] with Serializable

  object Remove {
    def apply[T, E](implicit remove: Remove[T, E]): Aux[T, E, remove.Out] = remove

    type Aux[T, U, Out0] = Remove[T, U] { type Out = Out0 }

    implicit def removeTuple[T, L1 <: HList, U, L2 <: HList]
      (implicit gen: Generic.Aux[T, L1], remove: hl.Remove.Aux[L1, U, (U, L2)], tp: hl.Tupler[L2]): Aux[T, U, (U, tp.Out)] = new Remove[T, U] {
        type Out = (U, tp.Out)
        def apply(t: T): Out = { val (u, rem) = remove(gen.to(t)) ; (u, tp(rem)) }
      }
  }

  /**
   * Type class supporting removal of a sublist from this tuple. Available only if this tuple contains a
   * sublist of type `SL`.
   *
   * The elements of `SL` do not have to be contiguous in this tuple.
   *
   * @author Miles Sabin
   */
  trait RemoveAll[T, S] extends DepFn1[T] with Serializable

  object RemoveAll {
    def apply[T, S](implicit remove: RemoveAll[T, S]): Aux[T, S, remove.Out] = remove

    type Aux[T, S, Out0] = RemoveAll[T, S] { type Out = Out0 }

    implicit def removeAllTuple[T, ST, SL <: HList, L1 <: HList, L2 <: HList]
      (implicit gent: Generic.Aux[T, L1], gens: Generic.Aux[ST, SL],  removeAll: hl.RemoveAll.Aux[L1, SL, (SL, L2)], tp: hl.Tupler[L2]): Aux[T, ST, (ST, tp.Out)] =
        new RemoveAll[T, ST] {
          type Out = (ST, tp.Out)
          def apply(t: T): Out = { val (e, rem) = removeAll(gent.to(t)) ; (gens.from(e), tp(rem)) }
        }
  }

  /**
   * Type class supporting replacement of the first element of type V from this tuple with an element of type U.
   * Available only if this tuple contains an element of type `V`.
   *
   * @author Miles Sabin
   */
  trait Replacer[T, U, V] extends DepFn2[T, U] with Serializable

  object Replacer {
    def apply[T, U, V](implicit replacer: Replacer[T, U, V]): Aux[T, U, V, replacer.Out] = replacer

    type Aux[T, U, V, Out0] = Replacer[T, U, V] { type Out = Out0 }

    implicit def replaceTuple[T, L1 <: HList, U, V, L2 <: HList]
      (implicit gen: Generic.Aux[T, L1], replace: hl.Replacer.Aux[L1, V, U, (V, L2)], tp: hl.Tupler[L2]): Aux[T, U, V, (V, tp.Out)] = new Replacer[T, U, V] {
        type Out = (V, tp.Out)
        def apply(t: T, u: U): Out = { val (v, rep) = replace(gen.to(t), u) ; (v, tp(rep)) }
      }
  }

  /**
   * Type class supporting replacement of the Nth element of this tuple with an element of type V. Available only if
   * this tuple contains at least N elements.
   *
   * @author Miles Sabin
   */
  trait ReplaceAt[T, N <: Nat, U] extends DepFn2[T, U] with Serializable

  object ReplaceAt {
    def apply[T, N <: Nat, V](implicit replacer: ReplaceAt[T, N, V]): Aux[T, N, V, replacer.Out] = replacer

    type Aux[T, N <: Nat, U, Out0] = ReplaceAt[T, N, U] { type Out = Out0 }

    implicit def replaceTuple[T, L1 <: HList, N <: Nat, U, V, L2 <: HList]
      (implicit gen: Generic.Aux[T, L1], replaceAt: hl.ReplaceAt.Aux[L1, N, U, (V, L2)], tp: hl.Tupler[L2]): Aux[T, N, U, (V, tp.Out)] = new ReplaceAt[T, N, U] {
        type Out = (V, tp.Out)
        def apply(t: T, u: U): Out = { val (v, rep) = replaceAt(gen.to(t), u) ; (v, tp(rep)) }
      }
  }

  /**
   * Type class supporting replacement of the first element of type U from this tuple with the result of
   * its transformation via a given function into a new element of type V.
   * Available only if this tuple contains an element of type `U`.
   *
   * @author Howard Branch
   */
  trait Modifier[T, U, V] extends DepFn2[T, U => V] with Serializable

  object Modifier {
    def apply[T, U, V](implicit modifier: Modifier[T, U, V]): Aux[T, U, V, modifier.Out] = modifier

    type Aux[T, U, V, Out0] = Modifier[T, U, V] { type Out = Out0 }

    implicit def modifyTuple[T, L1 <: HList, U, V, L2 <: HList]
    (implicit gen: Generic.Aux[T, L1], modify: hl.Modifier.Aux[L1, U, V, (U, L2)], tp: hl.Tupler[L2]): Aux[T, U, V, (U, tp.Out)] = new Modifier[T, U, V] {
      type Out = (U, tp.Out)
      def apply(t: T, f: U => V): Out = { val (u, rep) = modify(gen.to(t), f) ; (u, tp(rep)) }
    }
  }

  /**
   * Type class supporting replacement of the `N`th element of this `Tuple` with the result of
   * calling `F` on it.
   * Available only if this `Tuple` contains at least `N` elements.
   *
   * @author Andreas Koestler
   */
  trait ModifierAt[T, N <: Nat, U, V] extends DepFn2[T, U => V]

  object ModifierAt {
    def apply[T, N <: Nat, U, V](implicit modifier: ModifierAt[T, N, U, V]): Aux[T, N, U, V, modifier.Out] = modifier

    type Aux[T, N <: Nat, U, V, Out0] = ModifierAt[T, N, U, V] {type Out = Out0}

    implicit def modifyTuple[S, T, U, V, N <: Nat, L <: HList, OutL <: HList]
    (implicit
     gen: Generic.Aux[T, L],
     modifier: hl.ModifierAt.Aux[L, N, U, V, (S, OutL)],
     tup: hl.Tupler[OutL]
      ): Aux[T, N, U, V, (S, tup.Out)] = new ModifierAt[T, N, U, V] {

      type Out = (S, tup.Out)

      def apply(t: T, f: U => V) = {
        val (u, rep) = modifier(gen.to(t), f);
        (u, tup(rep))
      }
    }
  }
  /**
   * Type class supporting retrieval of the first ''n'' elements of this tuple. Available only if this tuple has at
   * least ''n'' elements.
   *
   * @author Miles Sabin
   */
  trait Take[T, N <: Nat] extends DepFn1[T] with Serializable

  object Take {
    def apply[T, N <: Nat](implicit take: Take[T, N]): Aux[T, N, take.Out] = take

    type Aux[T, N <: Nat, Out0] = Take[T, N] { type Out = Out0 }

    implicit def tupleTake[T, L1 <: HList, N <: Nat, L2 <: HList]
      (implicit gen: Generic.Aux[T, L1], take: hl.Take.Aux[L1, N, L2], tp: hl.Tupler[L2]): Aux[T, N, tp.Out] =
        new Take[T, N] {
          type Out = tp.Out
          def apply(t: T): tp.Out = tp(take(gen.to(t)))
        }
  }

  /**
   * Type class supporting removal of the first ''n'' elements of this tuple. Available only if this tuple has at
   * least ''n'' elements.
   *
   * @author Miles Sabin
   */
  trait Drop[T, N <: Nat] extends DepFn1[T] with Serializable

  object Drop {
    def apply[T, N <: Nat](implicit drop: Drop[T, N]): Aux[T, N, drop.Out] = drop

    type Aux[T, N <: Nat, Out0] = Drop[T, N] { type Out = Out0 }

    implicit def tupleDrop[T, L1 <: HList, N <: Nat, L2 <: HList]
      (implicit gen: Generic.Aux[T, L1], drop: hl.Drop.Aux[L1, N, L2], tp: hl.Tupler[L2]): Aux[T, N, tp.Out] =
        new Drop[T, N] {
          type Out = tp.Out
          def apply(t: T): tp.Out = tp(drop(gen.to(t)))
        }
  }

  /**
   * Type class supporting splitting this tuple at the ''nth'' element returning the prefix and suffix as a pair.
   * Available only if this tuple has at least ''n'' elements.
   *
   * @author Miles Sabin
   */
  trait Split[T, N <: Nat] extends DepFn1[T] with Serializable

  object Split {
    def apply[T, N <: Nat](implicit split: Split[T, N]): Aux[T, N, split.Out] = split

    type Aux[T, N <: Nat, Out0] = Split[T, N] { type Out = Out0 }

    implicit def tupleSplit[T, L <: HList, N <: Nat, LP <: HList, LS <: HList]
      (implicit
        gen: Generic.Aux[T, L],
        split: hl.Split.Aux[L, N, LP, LS],
        tpp: hl.Tupler[LP],
        tps: hl.Tupler[LS]
      ): Aux[T, N, (tpp.Out, tps.Out)] =
        new Split[T, N] {
          type Out = (tpp.Out, tps.Out)
          def apply(t: T): Out = { val p :: s :: HNil = split.product(gen.to(t)) ; (tpp(p), tps(s)) }
        }
  }

  /**
   * Type class supporting splitting this tuple at the ''nth'' element returning the reverse prefix and suffix as a
   * pair. Available only if this tuple has at least ''n'' elements.
   *
   * @author Miles Sabin
   */
  trait ReverseSplit[T, N <: Nat] extends DepFn1[T] with Serializable

  object ReverseSplit {
    def apply[T, N <: Nat](implicit split: ReverseSplit[T, N]): Aux[T, N, split.Out] = split

    type Aux[T, N <: Nat, Out0] = ReverseSplit[T, N] { type Out = Out0 }

    implicit def tupleReverseSplit[T, L <: HList, N <: Nat, LP <: HList, LS <: HList]
      (implicit
        gen: Generic.Aux[T, L],
        split: hl.ReverseSplit.Aux[L, N, LP, LS],
        tpp: hl.Tupler[LP],
        tps: hl.Tupler[LS]
      ): Aux[T, N, (tpp.Out, tps.Out)] =
        new ReverseSplit[T, N] {
          type Out = (tpp.Out, tps.Out)
          def apply(t: T): Out = { val p :: s :: HNil = split.product(gen.to(t)) ; (tpp(p), tps(s)) }
        }
  }

  /**
   * Type class supporting splitting this tuple at the first occurrence of an element of type `U` returning the prefix
   * and suffix as a pair. Available only if this tuple contains an element of type `U`.
   *
   * @author Miles Sabin
   */
  trait SplitLeft[T, U] extends DepFn1[T] with Serializable

  object SplitLeft {
    def apply[T, U](implicit split: SplitLeft[T, U]): Aux[T, U, split.Out] = split

    type Aux[T, U, Out0] = SplitLeft[T, U] { type Out = Out0 }

    implicit def tupleSplitLeft[T, L <: HList, U, LP <: HList, LS <: HList]
      (implicit
        gen: Generic.Aux[T, L],
        split: hl.SplitLeft.Aux[L, U, LP, LS],
        tpp: hl.Tupler[LP],
        tps: hl.Tupler[LS]
      ): Aux[T, U, (tpp.Out, tps.Out)] =
        new SplitLeft[T, U] {
          type Out = (tpp.Out, tps.Out)
          def apply(t: T): Out = { val p :: s :: HNil = split.product(gen.to(t)) ; (tpp(p), tps(s)) }
        }
  }

  /**
   * Type class supporting splitting this tuple at the first occurrence of an element of type `U` returning the reverse
   * prefix and suffix as a pair. Available only if this tuple contains an element of type `U`.
   *
   * @author Miles Sabin
   */
  trait ReverseSplitLeft[T, U] extends DepFn1[T] with Serializable

  object ReverseSplitLeft {
    def apply[T, U](implicit split: ReverseSplitLeft[T, U]): Aux[T, U, split.Out] = split

    type Aux[T, U, Out0] = ReverseSplitLeft[T, U] { type Out = Out0 }

    implicit def tupleReverseSplitLeft[T, L <: HList, U, LP <: HList, LS <: HList]
      (implicit
        gen: Generic.Aux[T, L],
        split: hl.ReverseSplitLeft.Aux[L, U, LP, LS],
        tpp: hl.Tupler[LP],
        tps: hl.Tupler[LS]
      ): Aux[T, U, (tpp.Out, tps.Out)] =
        new ReverseSplitLeft[T, U] {
          type Out = (tpp.Out, tps.Out)
          def apply(t: T): Out = { val p :: s :: HNil = split.product(gen.to(t)) ; (tpp(p), tps(s)) }
        }
  }

  /**
   * Type class supporting splitting this tuple at the last occurrence of an element of type `U` returning the prefix
   * and suffix as a pair. Available only if this tuple contains an element of type `U`.
   *
   * @author Miles Sabin
   */
  trait SplitRight[T, U] extends DepFn1[T] with Serializable

  object SplitRight {
    def apply[T, U](implicit split: SplitRight[T, U]): Aux[T, U, split.Out] = split

    type Aux[T, U, Out0] = SplitRight[T, U] { type Out = Out0 }

    implicit def tupleSplitRight[T, L <: HList, U, LP <: HList, LS <: HList]
      (implicit
        gen: Generic.Aux[T, L],
        split: hl.SplitRight.Aux[L, U, LP, LS],
        tpp: hl.Tupler[LP],
        tps: hl.Tupler[LS]
      ): Aux[T, U, (tpp.Out, tps.Out)] =
        new SplitRight[T, U] {
          type Out = (tpp.Out, tps.Out)
          def apply(t: T): Out = { val p :: s :: HNil = split.product(gen.to(t)) ; (tpp(p), tps(s)) }
        }
  }

  /**
   * Type class supporting splitting this tuple at the last occurrence of an element of type `U` returning the reverse
   * prefix and suffix as a pair. Available only if this tuple contains an element of type `U`.
   *
   * @author Miles Sabin
   */
  trait ReverseSplitRight[T, U] extends DepFn1[T] with Serializable

  object ReverseSplitRight {
    def apply[T, U](implicit split: ReverseSplitRight[T, U]): Aux[T, U, split.Out] = split

    type Aux[T, U, Out0] = ReverseSplitRight[T, U] { type Out = Out0 }

    implicit def tupleReverseSplitRight[T, L <: HList, U, LP <: HList, LS <: HList]
      (implicit
        gen: Generic.Aux[T, L],
        split: hl.ReverseSplitRight.Aux[L, U, LP, LS],
        tpp: hl.Tupler[LP],
        tps: hl.Tupler[LS]
      ): Aux[T, U, (tpp.Out, tps.Out)] =
        new ReverseSplitRight[T, U] {
          type Out = (tpp.Out, tps.Out)
          def apply(t: T): Out = { val p :: s :: HNil = split.product(gen.to(t)) ; (tpp(p), tps(s)) }
        }
  }

  /**
   * Type class supporting reversing this tuple.
   *
   * @author Miles Sabin
   */
  trait Reverse[T] extends DepFn1[T] with Serializable

  object Reverse {
    def apply[T](implicit reverse: Reverse[T]): Aux[T, reverse.Out] = reverse

    type Aux[T, Out0] = Reverse[T] { type Out = Out0 }

    implicit def tupleReverseAux[T, L1 <: HList, L2 <: HList, Out]
      (implicit gen: Generic.Aux[T, L1], reverse: hl.Reverse.Aux[L1, L2], tp: hl.Tupler[L2]): Aux[T, tp.Out] =
        new Reverse[T] {
          type Out = tp.Out
          def apply(t: T): tp.Out = tp(reverse(gen.to(t)))
        }
  }

  /**
   * Type class supporting mapping a higher ranked function over this tuple.
   *
   * @author Miles Sabin
   */
  trait Mapper[T, P] extends DepFn1[T] with Serializable

  object Mapper {
    def apply[T, P](implicit mapper: Mapper[T, P]): Aux[T, P, mapper.Out] = mapper

    type Aux[T, P, Out0] = Mapper[T, P] { type Out = Out0 }

    implicit def mapper[T, P, L1 <: HList, L2 <: HList]
      (implicit gen: Generic.Aux[T, L1], mapper: hl.Mapper.Aux[P, L1, L2], tp: hl.Tupler[L2]): Aux[T, P, tp.Out] =
        new Mapper[T, P] {
          type Out = tp.Out
          def apply(t: T): tp.Out = tp(mapper(gen.to(t)))
        }
  }

  /**
   * Type class supporting flatmapping a higher ranked function over this tuple.
   *
   * @author Miles Sabin
   */
  trait FlatMapper[T, P] extends DepFn1[T] with Serializable

  object FlatMapper {
    def apply[T, P](implicit mapper: FlatMapper[T, P]): Aux[T, P, mapper.Out] = mapper

    import poly.Compose

    type Aux[T, P, Out0] = FlatMapper[T, P] { type Out = Out0 }

    implicit def mapper[T, P, L1 <: HList, L2 <: HList]
      (implicit gen: Generic.Aux[T, L1], mapper: hl.FlatMapper.Aux[Compose[productElements.type, P], L1, L2], tp: hl.Tupler[L2]): Aux[T, P, tp.Out] =
        new FlatMapper[T, P] {
          type Out = tp.Out
          def apply(t: T): tp.Out = tp(mapper(gen.to(t)))
        }
  }

  /**
   * Type class supporting mapping a constant valued function over this tuple.
   *
   * @author Miles Sabin
   */
  trait ConstMapper[T, C] extends DepFn2[T, C] with Serializable

  object ConstMapper {
    def apply[T, C](implicit mapper: ConstMapper[T, C]): Aux[T, C, mapper.Out] = mapper

    type Aux[T, C, Out0] = ConstMapper[T, C] { type Out = Out0 }

    implicit def mapper[T, C, L1 <: HList, L2 <: HList]
      (implicit gen: Generic.Aux[T, L1], mapper: hl.ConstMapper.Aux[C, L1, L2], tp: hl.Tupler[L2]): Aux[T, C, tp.Out] =
        new ConstMapper[T, C] {
          type Out = tp.Out
          def apply(t: T, c: C): tp.Out = tp(mapper(c, gen.to(t)))
        }
  }

  /**
   * Type class supporting mapping a polymorphic function over this tuple and then folding the result using a
   * monomorphic function value.
   *
   * @author Miles Sabin
   */
  trait MapFolder[T, R, P] extends Serializable { // Nb. Not a dependent function signature
    def apply(t: T, in: R, op: (R, R) => R): R
  }

  object MapFolder {
    def apply[T, R, P](implicit folder: MapFolder[T, R, P]) = folder

    implicit def mapper[T, L <: HList, R, P]
      (implicit gen: Generic.Aux[T, L], mapper: hl.MapFolder[L, R, P]): MapFolder[T, R, P] =
        new MapFolder[T, R, P] {
          def apply(t: T, in: R, op: (R, R) => R): R = mapper(gen.to(t), in, op)
        }
  }

  /**
   * Type class supporting left-folding a polymorphic binary function over this tuple.
   *
   * @author Miles Sabin
   */
  trait LeftFolder[T, U, P] extends DepFn2[T, U] with Serializable

  object LeftFolder {
    def apply[T, U, P](implicit folder: LeftFolder[T, U, P]): Aux[T, U, P, folder.Out] = folder

    type Aux[T, U, P, Out0] = LeftFolder[T, U, P] { type Out = Out0 }

    implicit def folder[T, L <: HList, U, P]
      (implicit gen: Generic.Aux[T, L], folder: hl.LeftFolder[L, U, P]): Aux[T, U, P, folder.Out] =
        new LeftFolder[T, U, P] {
          type Out = folder.Out
          def apply(t: T, u: U): Out = folder(gen.to(t), u)
        }
  }

  /**
   * Type class supporting right-folding a polymorphic binary function over this tuple.
   *
   * @author Miles Sabin
   */
  trait RightFolder[T, U, P] extends DepFn2[T, U] with Serializable

  object RightFolder {
    def apply[T, U, P](implicit folder: RightFolder[T, U, P]): Aux[T, U, P, folder.Out] = folder

    type Aux[T, U, P, Out0] = RightFolder[T, U, P] { type Out = Out0 }

    implicit def folder[T, L <: HList, U, P]
      (implicit gen: Generic.Aux[T, L], folder: hl.RightFolder[L, U, P]): Aux[T, U, P, folder.Out] =
        new RightFolder[T, U, P] {
          type Out = folder.Out
          def apply(t: T, u: U): Out = folder(gen.to(t), u)
        }
  }

  /**
   * Type class supporting left-reducing a polymorphic binary function over this tuple.
   *
   * @author Miles Sabin
   */
  trait LeftReducer[T, P] extends DepFn1[T] with Serializable

  object LeftReducer {
    def apply[T, P](implicit reducer: LeftReducer[T, P]): Aux[T, P, reducer.Out] = reducer

    type Aux[T, P, Out0] = LeftReducer[T, P] { type Out = Out0 }

    implicit def folder[T, L <: HList, P]
      (implicit gen: Generic.Aux[T, L], folder: hl.LeftReducer[L, P]): Aux[T, P, folder.Out] =
        new LeftReducer[T, P] {
          type Out = folder.Out
          def apply(t: T): Out = folder(gen.to(t))
        }
  }

  /**
   * Type class supporting right-reducing a polymorphic binary function over this tuple.
   *
   * @author Miles Sabin
   */
  trait RightReducer[T, P] extends DepFn1[T] with Serializable

  object RightReducer {
    def apply[T, P](implicit reducer: RightReducer[T, P]): Aux[T, P, reducer.Out] = reducer

    type Aux[T, P, Out0] = RightReducer[T, P] { type Out = Out0 }

    implicit def folder[T, L <: HList, P]
      (implicit gen: Generic.Aux[T, L], folder: hl.RightReducer[L, P]): Aux[T, P, folder.Out] =
        new RightReducer[T, P] {
          type Out = folder.Out
          def apply(t: T): Out = folder(gen.to(t))
        }
  }

  /**
   * Type class supporting transposing this tuple.
   *
   * @author Miles Sabin
   */
  trait Transposer[T] extends DepFn1[T] with Serializable

  object Transposer {
    def apply[T](implicit transposer: Transposer[T]): Aux[T, transposer.Out] = transposer

    type Aux[T, Out0] = Transposer[T] { type Out = Out0 }

    implicit def transpose[T, L1 <: HList, L2 <: HList, L3 <: HList, L4 <: HList]
      (implicit
        gen: Generic.Aux[T, L1],
        mpe: hl.Mapper.Aux[productElements.type, L1, L2],
        tps: hl.Transposer.Aux[L2, L3],
        mtp: hl.Mapper.Aux[tupled.type, L3, L4],
        tp:  hl.Tupler[L4]
      ): Aux[T, tp.Out] =
      new Transposer[T] {
        type Out = tp.Out
        def apply(t: T): Out = ((gen.to(t) map productElements).transpose map tupled).tupled
      }
  }

  /**
   * Type class supporting zipping this this tuple of monomorphic function values with its argument tuple of
   * correspondingly typed function arguments returning the result of each application as a tuple. Available only if
   * there is evidence that the corresponding function and argument elements have compatible types.
   *
   * @author Miles Sabin
   */
  trait ZipApply[FT, AT] extends DepFn2[FT, AT] with Serializable

  object ZipApply {
    def apply[FT, AT](implicit zip: ZipApply[FT, AT]): Aux[FT, AT, zip.Out] = zip

    type Aux[FT, AT, Out0] = ZipApply[FT, AT] { type Out = Out0 }

    implicit def zipApply[FT, FL <: HList, AT, AL <: HList, RL <: HList]
      (implicit
        genf: Generic.Aux[FT, FL],
        gena: Generic.Aux[AT, AL],
        zip:  hl.ZipApply.Aux[FL, AL, RL],
        tp:   hl.Tupler[RL]
      ): Aux[FT, AT, tp.Out] =
      new ZipApply[FT, AT] {
        type Out = tp.Out
        def apply(ft: FT, at: AT): Out = (genf.to(ft) zipApply gena.to(at)).tupled
      }
  }

  /**
   * Type class supporting zipping this tuple with a tuple of tuples returning a tuple of tuples with each
   * element of this tuple prepended to the corresponding tuple element of the argument tuple.
   *
   * @author Miles Sabin
   */
  trait ZipOne[H, T] extends DepFn2[H, T] with Serializable

  object ZipOne {
    def apply[H, T](implicit zip: ZipOne[H, T]): Aux[H, T, zip.Out] = zip

    type Aux[H, T, Out0] = ZipOne[H, T] { type Out = Out0 }

    implicit def zipOne[HT, HL <: HList, TT, TL <: HList, TLL <: HList, RLL <: HList, RL <: HList]
      (implicit
        genh: Generic.Aux[HT, HL],
        gent: Generic.Aux[TT, TL],
        mpet: hl.Mapper.Aux[productElements.type, TL, TLL],
        zone: hl.ZipOne.Aux[HL, TLL, RLL],
        mtp:  hl.Mapper.Aux[tupled.type, RLL, RL],
        tp:   hl.Tupler[RL]
      ): Aux[HT, TT, tp.Out] =
      new ZipOne[HT, TT] {
        type Out = tp.Out
        def apply(h: HT, t: TT): Out = ((genh.to(h) zipOne (gent.to(t) map productElements)) map tupled).tupled
      }
  }

  /**
   * Type class supporting zipping a tuple with a constant, resulting in a tuple of tuples of the form
   * ({element from input tuple}, {supplied constant})
   *
   * @author Miles Sabin
   */
  trait ZipConst[T, C] extends DepFn2[T, C] with Serializable

  object ZipConst {
    def apply[T, C](implicit zip: ZipConst[T, C]): Aux[T, C, zip.Out] = zip

    type Aux[T, C, Out0] = ZipConst[T, C] { type Out = Out0 }

    implicit def zipConst[T, C, L1 <: HList, L2 <: HList]
    (implicit gen: Generic.Aux[T, L1], zipper: hl.ZipConst.Aux[C, L1, L2], tp: hl.Tupler[L2]): Aux[T, C, tp.Out] =
      new ZipConst[T, C] {
        type Out = tp.Out
        def apply(t: T, c: C): tp.Out = tp(zipper(c, gen.to(t)))
      }
  }

  /**
   * Type class supporting zipping a tuple with its element indices, resulting in a tuple of tuples of the form
   * ({element from input tuple}, {element index})
   *
   * @author Andreas Koestler
   */
  trait ZipWithIndex[T] extends DepFn1[T] with Serializable

  object ZipWithIndex {
    def apply[T](implicit zip: ZipWithIndex[T]): Aux[T, zip.Out] = zip

    type Aux[T, Out0] = ZipWithIndex[T] { type Out = Out0 }

    implicit def zipConst[T, L1 <: HList, L2 <: HList]
    (implicit gen: Generic.Aux[T, L1], zipper: hl.ZipWithIndex.Aux[L1, L2], tp: hl.Tupler[L2]): Aux[T, tp.Out] =
      new ZipWithIndex[T] {
        type Out = tp.Out
        def apply(t: T): tp.Out = tp(zipper(gen.to(t)))
      }
  }


  /**
   * Type class supporting unification of this tuple.
   *
   * @author Miles Sabin
   */
  trait Unifier[T] extends DepFn1[T] with Serializable

  object Unifier {
    def apply[T](implicit unifier: Unifier[T]): Aux[T, unifier.Out] = unifier

    type Aux[T, Out0] = Unifier[T] { type Out = Out0 }

    implicit def unifier[T, L1 <: HList, L2 <: HList]
      (implicit gen: Generic.Aux[T, L1], unifier: hl.Unifier.Aux[L1, L2], tp: hl.Tupler[L2]): Aux[T, tp.Out] =
        new Unifier[T] {
          type Out = tp.Out
          def apply(t: T): Out = unifier(gen.to(t)).tupled
        }
  }

  /**
   * Type class supporting unification of all elements that are subtypes of `B` in this tuple to `B`, with all other
   * elements left unchanged.
   *
   * @author Miles Sabin
   */
  trait SubtypeUnifier[T, B] extends DepFn1[T] with Serializable

  object SubtypeUnifier {
    def apply[T, B](implicit unifier: SubtypeUnifier[T, B]): Aux[T, B, unifier.Out] = unifier

    type Aux[T, B, Out0] = SubtypeUnifier[T, B] { type Out = Out0 }

    implicit def subtypeUnifier[T, B, L1 <: HList, L2 <: HList]
      (implicit gen: Generic.Aux[T, L1], unifier: hl.SubtypeUnifier.Aux[L1, B, L2], tp: hl.Tupler[L2]): Aux[T, B, tp.Out] =
        new SubtypeUnifier[T, B] {
          type Out = tp.Out
          def apply(t: T): Out = unifier(gen.to(t)).tupled
        }
  }

  /**
   * Type class supporting computing the type-level Nat corresponding to the length of this tuple.
   *
   * @author Miles Sabin
   */
  trait Length[T] extends DepFn1[T] with Serializable

  object Length {
    def apply[T](implicit length: Length[T]): Aux[T, length.Out] = length

    type Aux[T, Out0] = Length[T] { type Out = Out0 }

    implicit def length[T, L <: HList]
      (implicit gen: Generic.Aux[T, L], length: hl.Length[L]): Aux[T, length.Out] =
        new Length[T] {
          type Out = length.Out
          def apply(t: T): Out = length()
        }
  }

  /**
   * Type class supporting conversion of this tuple to a `M` with elements typed as the least upper bound
   * of the types of the elements of this tuple.
   *
   * @author Alexandre Archambault
   */
  trait ToTraversable[T, M[_]] extends DepFn1[T] with Serializable {
    type Lub
    type Out = M[Lub]
  }

  object ToTraversable {
    def apply[T, M[_]]
      (implicit toTraversable: ToTraversable[T, M]): Aux[T, M, toTraversable.Lub] = toTraversable

    type Aux[T, M[_], Lub0] = ToTraversable[T, M] { type Lub = Lub0 }

    implicit def toTraversableNothing[M[_]](implicit tt: hl.ToTraversable.Aux[HNil, M, Nothing]): Aux[Unit, M, Nothing] =
      new ToTraversable[Unit, M] {
        type Lub = Nothing
        def apply(t: Unit) = tt(HNil)
      }

    implicit def toTraversable[T, L <: HList, M[_], Lub]
      (implicit gen: Generic.Aux[T, L], toTraversable: hl.ToTraversable.Aux[L, M, Lub]): Aux[T, M, Lub] =
        new ToTraversable[T, M] {
          type Lub = toTraversable.Lub
          def apply(t: T) = gen.to(t).to[M]
        }
  }

  /**
   * Type class supporting conversion of this tuple to a `List` with elements typed as the least upper bound
   * of the types of the elements of this tuple.
   *
   * Provided for backward compatibility.
   *
   * @author Miles Sabin
   */
  trait ToList[T, Lub] extends DepFn1[T] with Serializable

  object ToList {
    type Aux[T, Lub, Out0] = ToList[T, Lub] { type Out = Out0 }

    def apply[T, Lub](implicit toList: ToList[T, Lub]): Aux[T, Lub, toList.Out] = toList

    implicit def toList[T, Lub]
      (implicit toTraversable: ToTraversable.Aux[T, List, Lub]): Aux[T, Lub, List[Lub]] =
        new ToList[T, Lub] {
          type Out = List[Lub]
          def apply(t: T) = toTraversable(t)
        }

    implicit def toListNothing[T]
      (implicit toTraversable: ToTraversable.Aux[T, List, Nothing]): Aux[T, Nothing, List[Nothing]] =
        toList[T, Nothing]
  }

  /**
   * Type class supporting conversion of this tuple to an `Array` with elements typed as the least upper bound
   * of the types of the elements of this tuple.
   *
   * Provided for backward compatibility.
   *
   * @author Miles Sabin
   */
  trait ToArray[T, Lub] extends DepFn1[T]

  object ToArray {
    type Aux[T, Lub, Out0] = ToArray[T, Lub] { type Out = Out0 }

    def apply[T, Lub](implicit toArray: ToArray[T, Lub]): Aux[T, Lub, toArray.Out] = toArray

    implicit def toArray[T, Lub]
      (implicit toTraversable: ToTraversable.Aux[T, Array, Lub]): Aux[T, Lub, Array[Lub]] =
        new ToArray[T, Lub] {
          type Out = Array[Lub]
          def apply(t: T) = toTraversable(t)
        }

    implicit def toArrayNothing[T]
      (implicit toTraversable: ToTraversable.Aux[T, Array, Nothing]): Aux[T, Nothing, Array[Nothing]] =
        toArray[T, Nothing]
  }

  /**
   * Type class supporting conversion of this tuple to a `Sized[M[Lub], N]` with elements typed as
   * the least upper bound Lub of the types of the elements of this tuple.
   *
   * @author Alexandre Archambault
   */
  trait ToSized[T, M[_]] extends DepFn1[T] with Serializable

  object ToSized {
    def apply[T, M[_]](implicit toSized: ToSized[T, M]): Aux[T, M, toSized.Out] = toSized

    type Aux[T, M[_], Out0] = ToSized[T, M] { type Out = Out0 }

    implicit def toSized[T, L <: HList, M[_]]
      (implicit gen: Generic.Aux[T, L], toSized: hl.ToSized[L, M]): Aux[T, M, toSized.Out] =
        new ToSized[T, M] {
          type Out = toSized.Out
          def apply(t: T) = gen.to(t).toSized[M]
        }
  }

  /**
   * Type class computing the coproduct type corresponding to this tuple.
   *
   * @author Andreas Koestler
   */
  trait ToCoproduct[T] extends Serializable { type Out <: Coproduct }

  object ToCoproduct {
    def apply[T](implicit tcp: ToCoproduct[T]): Aux[T, tcp.Out] = tcp

    type Aux[T, Out0 <: Coproduct] = ToCoproduct[T] {type Out = Out0}

    implicit val hnilToCoproduct: Aux[HNil, CNil] =
      new ToCoproduct[HNil] {
        type Out = CNil
      }

    implicit def hlistToCoproduct[T, L <: HList](implicit
                                                 gen: Generic.Aux[T, L],
                                                 ut: hl.ToCoproduct[L]
                                                  ): Aux[T, ut.Out] =
      new ToCoproduct[T] {
        type Out = ut.Out
      }
  }

  /**
   * Type class computing the sum type corresponding to this tuple.
   *
   * @author Andreas Koestler
   */
  trait ToSum[T] extends Serializable { type Out <: Coproduct }

  object ToSum {
    def apply[T](implicit tcp: ToSum[T]): Aux[T, tcp.Out] = tcp

    type Aux[T, Out0 <: Coproduct] = ToSum[T] {type Out = Out0}

    implicit val hnilToSum: Aux[HNil, CNil] =
      new ToSum[HNil] {
        type Out = CNil
      }

    implicit def hlistToSum[T, L <: HList](implicit
                                           gen: Generic.Aux[T, L],
                                           ut: hl.ToSum[L]
                                            ): Aux[T, ut.Out] =
      new ToSum[T] {
        type Out = ut.Out
      }
  }


  /**
   * Type Class witnessing that this tuple can be collected with a 'Poly' to produce a new tuple
   *
   * @author Stacy Curl
   */
  trait Collect[T, P <: Poly] extends DepFn1[T] with Serializable

  object Collect {
    def apply[T, P <: Poly](implicit collect: Collect[T, P]): Aux[T, P, collect.Out] = collect

    type Aux[T, P <: Poly, Out0] = Collect[T, P] { type Out = Out0 }

    implicit def collect[T, L <: HList, L2 <: HList, P <: Poly]
      (implicit gen: Generic.Aux[T, L], collect: hl.Collect.Aux[L, P, L2], tp: hl.Tupler[L2])
        : Aux[T, P, tp.Out] = new Collect[T, P] {
          type Out = tp.Out

          def apply(t: T): tp.Out = tp(collect(gen.to(t)))
        }
  }

  /**
   * Typer class supporting the calculation of every permutation of this tuple
   *
   * @author Stacy Curl
   */
  trait Permutations[T] extends DepFn1[T] with Serializable

  object Permutations {
    def apply[T](implicit permutations: Permutations[T]): Aux[T, permutations.Out] = permutations

    type Aux[T, Out0] = Permutations[T] { type Out = Out0 }

    implicit def permutations[T, L <: HList, L2 <: HList, L3 <: HList]
      (implicit gen: Generic.Aux[T, L], collect: hl.Permutations.Aux[L, L2],
        mapper: hl.Mapper.Aux[tupled.type, L2, L3], tp: hl.Tupler[L3]
      ): Aux[T, tp.Out] = new Permutations[T] {
        type Out = tp.Out

        def apply(t: T): Out = tp(collect(gen.to(t)).map(tupled))
      }
  }

  /**
   * Type class supporting rotating a tuple left
   *
   * @author Stacy Curl
   */
  trait RotateLeft[T, N <: Nat] extends DepFn1[T] with Serializable

  object RotateLeft {
    def apply[T, N <: Nat](implicit rotateLeft: RotateLeft[T, N]): Aux[T, N, rotateLeft.Out] = rotateLeft

    type Aux[T, N <: Nat, Out0] = RotateLeft[T, N] { type Out = Out0 }

    implicit def tupleRotateLeft[T, N <: Nat, L <: HList, L2 <: HList]
      (implicit gen: Generic.Aux[T, L], rotateLeft: hl.RotateLeft.Aux[L, N, L2], tp: hl.Tupler[L2])
        : Aux[T, N, tp.Out] = new RotateLeft[T, N] {
          type Out = tp.Out

          def apply(t: T): Out = tp(rotateLeft(gen.to(t)))
        }
  }

  /**
   * Type class supporting rotating a tuple right
   *
   * @author Stacy Curl
   */
  trait RotateRight[T, N <: Nat] extends DepFn1[T] with Serializable

  object RotateRight {
    def apply[T, N <: Nat](implicit rotateRight: RotateRight[T, N]): Aux[T, N, rotateRight.Out] = rotateRight

    type Aux[T, N <: Nat, Out0] = RotateRight[T, N] { type Out = Out0 }

    implicit def tupleRotateRight[T, N <: Nat, L <: HList, L2 <: HList]
      (implicit gen: Generic.Aux[T, L], rotateRight: hl.RotateRight.Aux[L, N, L2], tp: hl.Tupler[L2])
        : Aux[T, N, tp.Out] = new RotateRight[T, N] {
          type Out = tp.Out

          def apply(t: T): Out = tp(rotateRight(gen.to(t)))
        }
  }

  /**
   * Type class supporting left-scanning a binary polymorphic function over this tuple.
   *
   * @author Owein Reese
   */
  trait LeftScanner[T, In, P <: Poly] extends DepFn2[T, In] with Serializable

  object LeftScanner{
    def apply[T, In, P <: Poly](implicit scanL: LeftScanner[T, In, P]): Aux[T, In, P, scanL.Out] = scanL

    type Aux[T, In, P <: Poly, Out0] = LeftScanner[T, In, P] { type Out = Out0 }

    implicit def scanner[T, L <: HList, In, P <: Poly, R <: HList]
      (implicit gen: Generic.Aux[T, L],
        scanL: hl.LeftScanner.Aux[L, In, P, R],
        tp: hl.Tupler[R]
      ): Aux[T, In, P, tp.Out] =
        new LeftScanner[T, In, P] {
          type Out = tp.Out

          def apply(t: T, in: In): Out = tp(scanL(gen.to(t), in))
        }
  }

  /**
   * Type class supporting right-scanning a binary polymorphic function over this tuple.
   *
   * @author Owein Reese
   */
  trait RightScanner[T, In, P <: Poly] extends DepFn2[T, In] with Serializable

  object RightScanner{
    def apply[T, In, P <: Poly](implicit scanR: RightScanner[T, In, P]): Aux[T, In, P, scanR.Out] = scanR

    type Aux[T, In, P <: Poly, Out0] = RightScanner[T, In, P] { type Out = Out0 }

    implicit def scanner[T, L <: HList, In, P <: Poly, R <: HList]
      (implicit gen: Generic.Aux[T, L],
        scanR: hl.RightScanner.Aux[L, In, P, R],
        tp: hl.Tupler[R]
      ): Aux[T, In, P, tp.Out] =
        new RightScanner[T, In, P] {
          type Out = tp.Out

          def apply(t: T, in: In): Out = tp(scanR(gen.to(t), in))
        }
  }

  /**
   * Type class supporting producing a tuple of shape `N` filled with elements of type `A`.
   *
   * @author Alexandre Archambault
   */
  trait Fill[N, A] extends DepFn1[A] with Serializable

  object Fill {
    def apply[N, A](implicit fill: Fill[N, A]) = fill

    type Aux[N, A, Out0] = Fill[N, A] { type Out = Out0 }

    implicit def fill1[N <: Nat, A, L <: HList, P]
      (implicit fill: hlist.Fill.Aux[N, A, L], tupler: hlist.Tupler[L]): Aux[N, A, tupler.Out] =
        new Fill[N, A] {
          type Out = tupler.Out
          def apply(elem: A) = tupler(fill(elem))
        }

    implicit def fill2[A, N1 <: Nat, N2 <: Nat, SubOut]
      (implicit subFill: Fill.Aux[N2, A, SubOut], fill: Fill[N1, SubOut]): Aux[(N1, N2), A, fill.Out] =
        new Fill[(N1, N2), A] {
          type Out = fill.Out
          def apply(elem: A) = fill(subFill(elem))
        }

  }

  /**
   * Type class supporting the patching of a tuple.
   *
   * @author Owein Reese
   */
  trait Patcher[N <: Nat, M <: Nat, T, InT] extends DepFn2[T, InT] with Serializable

  object Patcher{
    def apply[N <: Nat, M <: Nat, T, InT](implicit patch: Patcher[N, M, T, InT]) = patch

    implicit def tuplePatch[N <: Nat, M <: Nat, T, L <: HList, InT, InL <: HList, OutL <: HList]
      (implicit gen: Generic.Aux[T, L],
        genIn: Generic.Aux[InT, InL],
        patch: hl.Patcher.Aux[N, M, L, InL, OutL],
        tp: hl.Tupler[OutL]) =
        new Patcher[N, M, T, InT]{
          type Out = tp.Out

          def apply(t: T, in: InT) = tp(patch(gen.to(t), genIn.to(in)))
        }
  }

  /**
   * Typeclass supporting grouping this `Tuple` into tuples of `N` items each, at `Step`
   * apart. If `Step` equals `N` then the groups do not overlap.
   *
   * @author Andreas Koestler
   */
  trait Grouper[T, N <: Nat, Step <: Nat] extends DepFn1[T] with Serializable

  object Grouper {
    def apply[T, N <: Nat, Step <: Nat](implicit grouper: Grouper[T, N, Step]): Aux[T, N, Step, grouper.Out] = grouper

    type Aux[T, N <: Nat, Step <: Nat, Out0] = Grouper[T, N, Step] {type Out = Out0}

    implicit def tupleGrouper[T, N <: Nat, Step <: Nat, L <: HList, OutL <: HList]
    (implicit
     gen: Generic.Aux[T, L],
     grouper: hl.Grouper.Aux[L, N, Step, OutL],
     tupler: hl.Tupler[OutL]
      ): Aux[T, N, Step, tupler.Out] = new Grouper[T, N, Step] {
      type Out = tupler.Out

      def apply(t: T): Out = tupler(grouper(gen.to(t)))
    }

  }

  /**
   * Typeclass supporting grouping this `Tuple` into tuples of `N` items each, at `Step`
   * apart. If `Step` equals `N` then the groups do not overlap.
   *
   * Use the elements in `Pad` as necessary to complete last partition
   * up to `n` items. In case there are not enough padding elements, return a partition
   * with less than `n` items.
   *
   * @author Andreas Koestler
   */
  trait PaddedGrouper[T, N <: Nat, Step <: Nat, Pad] extends DepFn2[T, Pad] with Serializable

  object PaddedGrouper {
    def apply[T, N <: Nat, Step <: Nat, Pad](implicit
                                             grouper: PaddedGrouper[T, N, Step, Pad]
                                              ): Aux[T, N, Step, Pad, grouper.Out] = grouper

    type Aux[T, N <: Nat, Step <: Nat, Pad, Out0] = PaddedGrouper[T, N, Step, Pad] {type Out = Out0}

    implicit def tuplePaddedGrouper[Pad, PadL <: HList, T, N <: Nat, Step <: Nat, L <: HList, OutL <: HList]
    (implicit
     genL: Generic.Aux[T, L],
     genPad: Generic.Aux[Pad, PadL],
     grouper: hl.PaddedGrouper.Aux[L, N, Step, PadL, OutL],
     tupler: hl.Tupler[OutL]
      ): Aux[T, N, Step, Pad, tupler.Out] = new PaddedGrouper[T, N, Step, Pad] {
      type Out = tupler.Out

      def apply(t: T, pad: Pad): Out = tupler(grouper(genL.to(t), genPad.to(pad)))
    }

  }

  /**
   * Type class supporting permuting this `Tuple` into the same order as another `Tuple` with
   * the same element types.
   *
   * @author Peter Neyens
   */
  @implicitNotFound("Implicit not found: shapeless.ops.tuple.Align[${T}, ${U}]. The types ${T} and ${U} cannot be aligned.")
  trait Align[T, U] extends (T => U) with Serializable {
    def apply(t: T): U
  }

  object Align {
    def apply[T, U](implicit align: Align[T, U]): Align[T, U] = align

    implicit def tupleAlign[T, U, L <: HList, M <: HList]
      (implicit 
        gent: Generic.Aux[T, L],
        genu: Generic.Aux[U, M],
        align: hl.Align[L, M], 
        tp: hl.Tupler.Aux[M, U]
      ): Align[T, U] = 
        new Align[T, U] {
          def apply(t: T): U = align(gent.to(t)).tupled
        }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy