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

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

The newest version!
/*
 * Copyright (c) 2011-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.tailrec
import scala.annotation.implicitNotFound
import scala.collection.{ BuildFrom, Factory, Iterable }
import scala.collection.generic.IsIterableLike
import scala.collection.mutable

import function._
import poly._

object hlist {
  /**
   * Type class witnessing that this `HList` is composite and providing access to head and tail.
   *
   * @author Miles Sabin
   */
  trait IsHCons[L <: HList] extends Serializable {
    type H
    type T <: HList

    def head(l : L) : H
    def tail(l : L) : T
    def cons(h : H, t : T) : L
  }

  object IsHCons {
    def apply[L <: HList](implicit isHCons: IsHCons[L]): Aux[L, isHCons.H, isHCons.T] = isHCons

    type Aux[L <: HList, H0, T0 <: HList] = IsHCons[L] { type H = H0; type T = T0 }
    implicit def hlistIsHCons[H0, T0 <: HList]: Aux[H0 :: T0, H0, T0] =
      new IsHCons[H0 :: T0] {
        type H = H0
        type T = T0

        def head(l : H0 :: T0) : H = l.head
        def tail(l : H0 :: T0) : T = l.tail
        def cons(h : H0, t : T0) : H0 :: T0 = h :: t
      }
  }

  /**
   * Type class witnessing that the result of wrapping each element of `HList` `L` in type constructor `F` is `Out`.
   */
  trait Mapped[L <: HList, F[_]] extends Serializable {
    type Out <: HList
  }

  object Mapped {
    def apply[L <: HList, F[_]](implicit mapped: Mapped[L, F]): Aux[L, F, mapped.Out] = mapped

    type Aux[L <: HList, F[_], Out0 <: HList] = Mapped[L, F] { type Out = Out0 }

    implicit def hnilMapped[F[_]]: Aux[HNil, F, HNil] = new Mapped[HNil, F] { type Out = HNil }

    implicit def hnilConstMapped[F]: Aux[HNil, Const[F]#λ, HNil] = new Mapped[HNil, Const[F]#λ] { type Out = HNil }

    implicit def hlistIdMapped[H, T <: HList]: Aux[H :: T, Id, H :: T] = new Mapped[H :: T, Id] { type Out = H :: T }

    implicit def hlistMapped1[H, T <: HList, F[_], OutM <: HList](implicit mt : Mapped.Aux[T, F, OutM]): Aux[H :: T, F, F[H] :: OutM] =
      new Mapped[H :: T, F] { type Out = F[H] :: OutM }

    implicit def hlistMapped2[H, T <: HList, F, OutM <: HList](implicit mt : Mapped.Aux[T, Const[F]#λ, OutM]): Aux[H :: T, Const[F]#λ, F :: OutM] =
      new Mapped[H :: T, Const[F]#λ] { type Out = F :: OutM }
  }

  /**
   * Type class witnessing that the result of stripping type constructor `F` off each element of `HList` `L` is `Out`.
   */
  trait Comapped[L <: HList, F[_]] extends Serializable {
    type Out <: HList
  }

  trait LowPriorityComapped {
    type Aux[L <: HList, F[_], Out0 <: HList] = Comapped[L, F] { type Out = Out0 }
    implicit def hlistIdComapped[L <: HList]: Aux[L, Id, L] = new Comapped[L, Id] { type Out = L }
  }

  object Comapped extends LowPriorityComapped {
    def apply[L <: HList, F[_]](implicit comapped: Comapped[L, F]): Aux[L, F, comapped.Out] = comapped

    implicit def hnilComapped[F[_]]: Aux[HNil, F, HNil] = new Comapped[HNil, F] { type Out = HNil }

    implicit def hlistComapped[H, T <: HList, F[_], TCM <: HList](
      implicit mt : Comapped.Aux[T, F, TCM]
    ): Aux[F[H] :: T, F, H :: TCM] =
      new Comapped[F[H] :: T, F] { type Out = H :: TCM }
  }

  /**
   * Type class witnessing that `HList`s `L1` and `L2` have elements of the form `F1[Ln]` and `F2[Ln]` respectively for all
   * indices `n`. This implies that a natural transform `F1 ~> F2` will take a list of type `L1` onto a list of type `L2`.
   *
   * @author Miles Sabin
   */
  trait NatTRel[L1 <: HList, F1[_], L2 <: HList, F2[_]] extends Serializable {
    def map(nt: F1 ~> F2, fa: L1): L2
  }

  object NatTRel {
    def apply[L1 <: HList, F1[_], L2 <: HList, F2[_]](implicit natTRel: NatTRel[L1, F1, L2, F2]) = natTRel

    implicit def hnilNatTRel1[F1[_], F2[_]] = new NatTRel[HNil, F1, HNil, F2] {
      def map(f: F1 ~> F2, fa: HNil): HNil = HNil
    }

    implicit def hnilNatTRel2[F1[_], H2] = new NatTRel[HNil, F1, HNil, Const[H2]#λ] {
      def map(f: F1 ~> Const[H2]#λ, fa: HNil): HNil = HNil
    }

    implicit def hlistNatTRel1[H, F1[_], F2[_], T1 <: HList, T2 <: HList](implicit nt : NatTRel[T1, F1, T2, F2]) =
      new NatTRel[F1[H] :: T1, F1, F2[H] :: T2, F2] {
        def map(f: F1 ~> F2, fa: F1[H] :: T1): F2[H] :: T2 = f(fa.head) :: nt.map(f, fa.tail)
      }

    implicit def hlistNatTRel2[H, F2[_], T1 <: HList, T2 <: HList](implicit nt : NatTRel[T1, Id, T2, F2]) =
      new NatTRel[H :: T1, Id, F2[H] :: T2, F2] {
        def map(f: Id ~> F2, fa: H :: T1): F2[H] :: T2 = f(fa.head) :: nt.map(f, fa.tail)
      }

    implicit def hlistNatTRel3[H, F1[_], T1 <: HList, T2 <: HList](implicit nt : NatTRel[T1, F1, T2, Id]) =
      new NatTRel[F1[H] :: T1, F1, H :: T2, Id] {
        def map(f: F1 ~> Id, fa: F1[H] :: T1): H :: T2 = f(fa.head) :: nt.map(f, fa.tail)
      }

    implicit def hlistNatTRel4[H1, F1[_], T1 <: HList, H2, T2 <: HList](implicit nt : NatTRel[T1, F1, T2, Const[H2]#λ]) =
      new NatTRel[F1[H1] :: T1, F1, H2 :: T2, Const[H2]#λ] {
        def map(f: F1 ~> Const[H2]#λ, fa: F1[H1] :: T1): H2 :: T2 = f(fa.head) :: nt.map(f, fa.tail)
      }

    implicit def hlistNatTRel5[H1, T1 <: HList, H2, T2 <: HList](implicit nt : NatTRel[T1, Id, T2, Const[H2]#λ]) =
      new NatTRel[H1 :: T1, Id, H2 :: T2, Const[H2]#λ] {
        def map(f: Id ~> Const[H2]#λ, fa: H1 :: T1): H2 :: T2 = f(fa.head) :: nt.map(f, fa.tail)
      }
  }

  /**
   * Type class providing minimally witnessed operations on `HList`s which can be derived from `L` by wrapping
   * each of its elements in a type constructor.
   */
  trait HKernel {
    type L <: HList
    type Mapped[G[_]] <: HList
    type Length <: Nat

    def map[F[_], G[_]](f: F ~> G, l: Mapped[F]): Mapped[G]

    def tabulate[C](from: Int)(f: Int => C): Mapped[Const[C]#λ]

    def toList[C](l: Mapped[Const[C]#λ]): List[C]

    def length: Int
  }

  trait HNilHKernel extends HKernel {
    type L = HNil
    type Mapped[G[_]] = HNil
    type Length = _0

    def map[F[_], G[_]](f: F ~> G, l: HNil): HNil = HNil

    def tabulate[C](from: Int)(f: Int => C): HNil = HNil

    def toList[C](l: HNil): List[C] = Nil

    def length: Int = 0
  }

  case object HNilHKernel extends HNilHKernel

  final case class HConsHKernel[H, T <: HKernel](tail: T) extends HKernel {
    type L = H :: tail.L
    type Mapped[G[_]] = G[H] :: tail.Mapped[G]
    type Length = Succ[tail.Length]

    def map[F[_], G[_]](f: F ~> G, l: F[H] :: tail.Mapped[F]): G[H] :: tail.Mapped[G] = f(l.head) :: tail.map(f, l.tail)

    def tabulate[C](from: Int)(f: Int => C): C :: tail.Mapped[Const[C]#λ] = f(from) :: tail.tabulate(from+1)(f)

    def toList[C](l: C :: tail.Mapped[Const[C]#λ]): List[C] = l.head :: tail.toList(l.tail)

    def length: Int = 1+tail.length
  }

  object HKernel {
    def apply[L <: HList](implicit mk: HKernelAux[L]): mk.Out = mk()
    def apply[L <: HList](l: L)(implicit mk: HKernelAux[L]): mk.Out = mk()
  }

  trait HKernelAux[L <: HList] extends DepFn0 { type Out <: HKernel }

  object HKernelAux {

    type Aux[L <: HList, Out0 <: HKernel] = HKernelAux[L] { type Out = Out0 }

    implicit def mkHNilHKernel = new HKernelAux[HNil] {
      type Out = HNilHKernel
      def apply() = HNilHKernel
    }

    implicit def mkHListHKernel[H, T <: HList, CtOut <: HKernel](implicit ct: HKernelAux.Aux[T, CtOut]) = new HKernelAux[H :: T] {
      type Out = HConsHKernel[H, CtOut]
      def apply() = HConsHKernel[H, CtOut](ct())
    }
  }

  /**
   * Type class computing the coproduct type corresponding to this `HList`.
   *
   * @author Miles Sabin
   */
  trait ToCoproduct[L <: HList] extends Serializable { type Out <: Coproduct }

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

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

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

    implicit def hlistToCoproduct[H, T <: HList, UtOut <: Coproduct](implicit ut: ToCoproduct.Aux[T, UtOut]): Aux[H :: T, H :+: UtOut] =
      new ToCoproduct[H :: T] {
        type Out = H :+: UtOut
      }
  }

  /**
   * Type class computing the sum type corresponding to this `HList`.
   *
   */
  trait lowPriorityToSum {
    implicit def hlistToSum[H, T <: HList, UtOut <: Coproduct](implicit ut: ToSum.Aux[T, UtOut]): ToSum.Aux[H :: T, H :+: UtOut] =
      new ToSum[H :: T] {
        type Out = H :+: UtOut
      }
  }

  trait ToSum[L <: HList] extends Serializable {
    type Out <: Coproduct
  }

  object ToSum extends lowPriorityToSum {
    def apply[L <: HList](implicit tcp: ToSum[L]): Aux[L, tcp.Out] = tcp

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

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

    implicit def hlistToSum[H, T <: HList, OutL <: HList, OutS <: Coproduct](implicit
                                                          fn: FilterNot.Aux[T, H, OutL],
                                                          ut: ToSum.Aux[OutL, OutS]): Aux[H :: T, H :+: OutS] =
      new ToSum[H :: T] {
        type Out = H :+: OutS
      }
  }

  /**
   * Type class supporting computing the type-level Nat corresponding to the length of this `HList`.
   *
   * @author Miles Sabin
   */
  trait Length[L <: HList] extends DepFn0 with Serializable { type Out <: Nat }

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

    import shapeless.nat._
    type Aux[L <: HList, N <: Nat] = Length[L] { type Out = N }
    implicit def hnilLength[L <: HNil]: Aux[L, _0] = new Length[L] {
      type Out = _0
      def apply(): Out = _0
    }

    implicit def hlistLength[H, T <: HList, N <: Nat](implicit lt : Aux[T, N], sn : Witness.Aux[Succ[N]]): Aux[H :: T, Succ[N]] = new Length[H :: T] {
      type Out = Succ[N]
      def apply(): Out = sn.value
    }
  }

  /**
   * Type class supporting mapping a higher ranked function over this `HList`.
   *
   * @author Miles Sabin
   */
  trait Mapper[HF, In <: HList] extends DepFn1[In] with Serializable { type Out <: HList }

  object Mapper {
    def apply[F, L <: HList](implicit mapper: Mapper[F, L]): Aux[F, L, mapper.Out] = mapper

    type Aux[HF, In <: HList, Out0 <: HList] = Mapper[HF, In] { type Out = Out0 }

    implicit def hnilMapper1[HF]: Aux[HF, HNil, HNil] =
      new Mapper[HF, HNil] {
        type Out = HNil
        def apply(l : HNil): Out = HNil
      }

    implicit def hlistMapper1[HF <: Poly, InH, InT <: HList, OutT <: HList]
      (implicit hc : Case1[HF, InH], mt : Mapper.Aux[HF, InT, OutT]): Aux[HF, InH :: InT, hc.Result :: OutT] =
        new Mapper[HF, InH :: InT] {
          type Out = hc.Result :: OutT
          def apply(l : InH :: InT): Out = hc(l.head) :: mt(l.tail)
        }
  }

  /**
   * Type class supporting flatmapping a higher ranked function over this `HList`.
   *
   * @author Miles Sabin
   */
  trait FlatMapper[HF, In <: HList] extends DepFn1[In] with Serializable { type Out <: HList }

  object FlatMapper {
    def apply[F, L <: HList](implicit mapper: FlatMapper[F, L]): Aux[F, L, mapper.Out] = mapper

    type Aux[HF, In <: HList, Out0 <: HList] = FlatMapper[HF, In] { type Out = Out0 }

    implicit def hnilFlatMapper1[HF]: Aux[HF, HNil, HNil] =
      new FlatMapper[HF, HNil] {
        type Out = HNil
        def apply(l : HNil): Out = HNil
      }

    implicit def hlistFlatMapper1[HF <: Poly, InH, OutH <: HList, InT <: HList, OutT <: HList, Out0 <: HList]
      (implicit
        hc : Case1.Aux[HF, InH, OutH],
        mt : FlatMapper.Aux[HF, InT, OutT],
        prepend : Prepend.Aux[OutH, OutT, Out0]
      ): Aux[HF, InH :: InT, Out0] =
        new FlatMapper[HF, InH :: InT] {
          type Out = Out0
          def apply(l : InH :: InT): Out = prepend(hc(l.head), mt(l.tail))
        }
  }

  /**
   * Type class supporting mapping a constant valued function over this `HList`.
   *
   * @author Miles Sabin
   */
  trait ConstMapper[C, L <: HList] extends DepFn2[C, L] with Serializable { type Out <: HList }

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

    type Aux[C, L <: HList, Out0 <: HList] = ConstMapper[C, L] { type Out = Out0 }

    implicit def hnilConstMapper[C]: Aux[C, HNil, HNil] =
      new ConstMapper[C, HNil] {
        type Out = HNil
        def apply(c : C, l : HNil): Out = l
      }

    implicit def hlistConstMapper[H, T <: HList, C, OutT <: HList]
    (implicit mct : ConstMapper.Aux[C, T, OutT]): Aux[C, H :: T, C :: OutT] =
        new ConstMapper[C, H :: T] {
          type Out = C :: OutT
          def apply(c : C, l : H :: T): Out = c :: mct(c, l.tail)
        }
  }

  /**
   * Type class supporting mapping a polymorphic function over this `HList` and then folding the result using a
   * monomorphic function value.
   *
   * @author Miles Sabin
   */
  trait MapFolder[L <: HList, R, HF] extends Serializable {
    def apply(l : L, in : R, op : (R, R) => R) : R
  }

  object MapFolder {
    def apply[L <: HList, R, F](implicit folder: MapFolder[L, R, F]): MapFolder[L, R, F] = folder

    implicit def hnilMapFolder[R, HF]: MapFolder[HNil, R, HF] = new MapFolder[HNil, R, HF] {
      def apply(l : HNil, in : R, op : (R, R) => R): R = in
    }

    implicit def hlistMapFolder[H, T <: HList, R, HF <: Poly]
      (implicit hc : Case1.Aux[HF, H, R], tf : MapFolder[T, R, HF]): MapFolder[H :: T, R, HF] =
        new MapFolder[H :: T, R, HF] {
          def apply(l : H :: T, in : R, op : (R, R) => R): R = op(hc(l.head), tf(l.tail, in, op))
        }
  }

  /**
   * Type class supporting left-folding a polymorphic binary function over this `HList`.
   *
   * @author Miles Sabin
   */
  trait LeftFolder[L <: HList, In, HF] extends DepFn2[L, In] with Serializable

  object LeftFolder {
    def apply[L <: HList, In, F](implicit folder: LeftFolder[L, In, F]): Aux[L, In, F, folder.Out] = folder

    type Aux[L <: HList, In, HF, Out0] = LeftFolder[L, In, HF] { type Out = Out0 }

    implicit def hnilLeftFolder[In, HF]: Aux[HNil, In , HF, In] =
      new LeftFolder[HNil, In, HF] {
        type Out = In
        def apply(l : HNil, in : In): Out = in
      }

    implicit def hlistLeftFolder[H, T <: HList, In, HF, OutH, FtOut]
      (implicit f : Case2.Aux[HF, In, H, OutH], ft : LeftFolder.Aux[T, OutH, HF, FtOut]): Aux[H :: T, In, HF, FtOut] =
        new LeftFolder[H :: T, In, HF] {
          type Out = FtOut
          def apply(l : H :: T, in : In) : Out = ft(l.tail, f(in, l.head))
        }
  }

  /**
   * Type class supporting right-folding a polymorphic binary function over this `HList`.
   *
   * @author Miles Sabin
   */
  trait RightFolder[L <: HList, In, HF] extends DepFn2[L, In] with Serializable

  object RightFolder {
    def apply[L <: HList, In, F](implicit folder: RightFolder[L, In, F]): Aux[L, In, F, folder.Out] = folder

    type Aux[L <: HList, In, HF, Out0] = RightFolder[L, In, HF] { type Out = Out0 }

    implicit def hnilRightFolder[In, HF]: Aux[HNil, In, HF, In] =
      new RightFolder[HNil, In, HF] {
        type Out = In
        def apply(l : HNil, in : In): Out = in
      }

    implicit def hlistRightFolder[H, T <: HList, In, HF, OutT]
      (implicit ft : RightFolder.Aux[T, In, HF, OutT], f : Case2[HF, H, OutT]): Aux[H :: T, In, HF, f.Result] =
        new RightFolder[H :: T, In, HF] {
          type Out = f.Result
          def apply(l : H :: T, in : In): Out = f(l.head, ft(l.tail, in))
        }
  }

  /**
   * Type class supporting left-reducing a polymorphic binary function over this `HList`.
   *
   * @author Miles Sabin
   */
  trait LeftReducer[L <: HList, HF] extends DepFn1[L] with Serializable

  object LeftReducer {
    def apply[L <: HList, F](implicit reducer: LeftReducer[L, F]): Aux[L, F, reducer.Out] = reducer

    type Aux[L <: HList, HF, Out0] = LeftReducer[L, HF] { type Out = Out0 }
    implicit def leftReducer[H, T <: HList, HF, FolderOut](implicit folder : LeftFolder.Aux[T, H, HF, FolderOut]): Aux[H :: T, HF, FolderOut] =
      new LeftReducer[H :: T, HF] {
        type Out = FolderOut
        def apply(l : H :: T) : Out = folder.apply(l.tail, l.head)
      }
  }

  /**
   * Type class supporting right-reducing a polymorphic binary function over this `HList`.
   *
   * @author Miles Sabin
   */
  trait RightReducer[L <: HList, HF] extends DepFn1[L] with Serializable

  object RightReducer {
    def apply[L <: HList, F](implicit reducer: RightReducer[L, F]): Aux[L, F, reducer.Out] = reducer

    type Aux[L <: HList, HF, Out0] = RightReducer[L, HF] { type Out = Out0 }

    implicit def hsingleRightReducer[H, HF]: Aux[H :: HNil, HF, H] =
      new RightReducer[H :: HNil, HF] {
        type Out = H
        def apply(l : H :: HNil): Out = l.head
      }

    implicit def hlistRightReducer[H, T <: HList, HF, OutT]
      (implicit rt : RightReducer.Aux[T, HF, OutT], f : Case2[HF, H, OutT]): Aux[H :: T, HF, f.Result] =
        new RightReducer[H :: T, HF] {
          type Out = f.Result
          def apply(l : H :: T): Out = f(l.head, rt(l.tail))
        }
  }

  /**
   * Type class supporting unification of this `HList`.
   *
   * @author Miles Sabin
   */
  trait Unifier[L <: HList] extends DepFn1[L] with Serializable { type Out <: HList }

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

    type Aux[L <: HList, Out0 <: HList] = Unifier[L] { type Out = Out0 }

    implicit val hnilUnifier: Aux[HNil, HNil] = new Unifier[HNil] {
      type Out = HNil
      def apply(l : HNil): Out = l
    }

    implicit def hsingleUnifier[T]: Aux[T :: HNil, T :: HNil] =
      new Unifier[T :: HNil] {
        type Out = T :: HNil
        def apply(l : T :: HNil): Out = l
      }

    implicit def hlistUnifier[H1, H2, L, T <: HList, LtOut <: HList]
      (implicit u : Lub[H1, H2, L], lt : Unifier.Aux[L :: T, LtOut]): Aux[H1 :: H2 :: T, L :: LtOut] =
        new Unifier[H1 :: H2 :: T] {
          type Out = L :: LtOut
          def apply(l : H1 :: H2 :: T): Out = u.left(l.head) :: lt(u.right(l.tail.head) :: l.tail.tail)
        }
  }

  /**
   * Type class supporting unification of all elements that are subtypes of `B` in this `HList` to `B`, with all other
   * elements left unchanged.
   *
   * @author Travis Brown
   */
  trait SubtypeUnifier[L <: HList, B] extends DepFn1[L] with Serializable { type Out <: HList }

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

    type Aux[L <: HList, B, Out0 <: HList] = SubtypeUnifier[L, B] { type Out = Out0 }

    implicit def hnilSubtypeUnifier[B]: Aux[HNil, B, HNil] =
      new SubtypeUnifier[HNil, B] {
        type Out = HNil
        def apply(l : HNil): Out = l
      }

    implicit def hlistSubtypeUnifier1[H, T <: HList, B, SutOut <: HList]
      (implicit st : H <:< B, sut: SubtypeUnifier.Aux[T, B, SutOut]): Aux[H :: T, B, B :: SutOut] =
        new SubtypeUnifier[H :: T, B] {
          type Out = B :: SutOut
          def apply(l : H :: T): Out = st(l.head) :: sut(l.tail)
        }

    implicit def hlistSubtypeUnifier2[H, T <: HList, B, SutOut <: HList]
      (implicit nst : H <:!< B, sut: SubtypeUnifier.Aux[T, B, SutOut]): Aux[H :: T, B, H :: SutOut] =
        new SubtypeUnifier[H :: T, B] {
          type Out = H :: SutOut
          def apply(l : H :: T): Out = l.head :: sut(l.tail)
        }
  }

  /**
   * Type class supporting conversion of this `HList` to a `M` with elements typed
   * as the least upper bound Lub of the types of the elements of this `HList`.
   *
   * Serializable if the `Factory`s it implicitly finds are too.
   * Note that the `Factory`s from the standard library are *not*
   * serializable. See the tests for how to make your own serializable
   * `Factory` available to `ToTraversable`.
   *
   * @author Alexandre Archambault
   */
  trait ToTraversable[L <: HList, M[_]] extends DepFn1[L] with Serializable {
    type Lub
    def builder(): mutable.Builder[Lub, M[Lub]]
    def append[LLub](l: L, b: mutable.Builder[LLub, M[LLub]], f: Lub => LLub): Unit

    type Out = M[Lub]
    def apply(l: L): Out = {
      val b = builder()
      append(l, b, identity)
      b.result()
    }
  }

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

    type Aux[L <: HList, M[_], Lub0] = ToTraversable[L, M] { type Lub = Lub0 }

    implicit def hnilToTraversable[L <: HNil, M[_], T]
      (implicit cbf : Factory[T, M[T]]) : Aux[L, M, T] =
        new ToTraversable[L, M] {
          type Lub = T
          def builder() = cbf.newBuilder
          def append[LLub](l : L, b : mutable.Builder[LLub, M[LLub]], f : Lub => LLub) = {}
        }

    implicit def hnilToTraversableNothing[L <: HNil, M[_]]
      (implicit cbf : Factory[Nothing, M[Nothing]]) : Aux[L, M, Nothing] =
        hnilToTraversable[L, M, Nothing]

    implicit def hsingleToTraversable[T, M[_], Lub0]
      (implicit ev : T <:< Lub0, cbf : Factory[Lub0, M[Lub0]]) : Aux[T :: HNil, M, Lub0] =
        new ToTraversable[T :: HNil, M] {
          type Lub = Lub0
          def builder() = cbf.newBuilder
          def append[LLub](l : T :: HNil, b : mutable.Builder[LLub, M[LLub]], f : Lub0 => LLub) = {
            b += f(l.head)
          }
        }

    implicit def hlistToTraversable[H1, H2, T <: HList, LubT, Lub0, M[_]]
      (implicit
       tttvs  : Aux[H2 :: T, M, LubT],
       u      : Lub[H1, LubT, Lub0],
       cbf    : Factory[Lub0, M[Lub0]]) : Aux[H1 :: H2 :: T, M, Lub0] =
        new ToTraversable[H1 :: H2 :: T, M] {
          type Lub = Lub0
          def builder() = cbf.newBuilder
          def append[LLub](l : H1 :: H2 :: T, b : mutable.Builder[LLub, M[LLub]], f : Lub0 => LLub): Unit = {
            b += f(u.left(l.head)); tttvs.append[LLub](l.tail, b, f compose u.right)
          }
        }
  }

  /**
    * Type class supporting conversion of this `HList` to a `M` with elements typed as
    * a minimal `Coproduct` Cop such as each type of the elements of L appears once in Cop.
    *
    * @author Valentin Kasas
    */
  trait ToCoproductTraversable[L <: HList, M[_]] extends DepFn1[L] with Serializable {
    type Cop <: Coproduct
    type Out = M[Cop]
  }

  trait LowPriorityToCoproductTraversable {
    type Aux[L <: HList, M[_], C <: Coproduct] = ToCoproductTraversable[L, M] {type Cop = C}

    /**
      * Auxiliary type class witnessing that type `T` is not a member of `Coproduct` `C`
      */
    trait NotIn[C <: Coproduct, T]

    object NotIn {
      implicit def notInCNil[T]: NotIn[CNil, T] = new NotIn[CNil, T] {}

      implicit def notInCCons[CH, CT <: Coproduct, T]
        (implicit
         diff: CH =:!= T,
         notInTail: NotIn[CT, T]): NotIn[CH :+: CT, T] = new NotIn[CH :+: CT, T]{}
    }

    implicit def hconsToCoproductTraversable1[LH, LT <: HList, M[_], CT <: Coproduct]
      (implicit
       coproductOfT  : Aux[LT, M, CT],
       cbf           : Factory[CT, M[CT]],
       asTraversable : M[CT] <:< Traversable[CT],
       injectOut     : coproduct.Inject[CT, LH]): Aux[LH :: LT, M, CT] =
        new ToCoproductTraversable[LH :: LT, M] {
          type Cop = CT
          def apply(l : LH :: LT): Out = {
            val builder = cbf.newBuilder
            builder += injectOut(l.head)
            asTraversable(coproductOfT(l.tail)).foreach( builder += _)
            builder.result()
          }
        }
  }

  object ToCoproductTraversable extends LowPriorityToCoproductTraversable{

    implicit def hnilToCoproductTraversable[L <: HNil, M[_]]
    (implicit cbf: Factory[CNil, M[CNil]]): Aux[L, M, CNil] =
      new ToCoproductTraversable[L, M] {
        type Cop = CNil
        def apply(l: L) = cbf.newBuilder.result()
      }


    implicit def hconsToCoproductTraversable0[LH, LT <: HList, M[_], CT <: Coproduct]
      (implicit
       coproductOfT  : Aux[LT, M, CT],
       cbf           : Factory[LH :+: CT, M[LH :+: CT]],
       asTraversable : M[CT] <:< Traversable[CT],
       notIn         : NotIn[CT, LH],
       injectOut     : coproduct.Inject[LH :+: CT, LH],
       basisTail     : coproduct.Basis[LH :+: CT,CT]): Aux[LH :: LT, M, LH :+: CT] =
        new ToCoproductTraversable[LH :: LT, M] {
          type Cop = LH :+: CT
          def apply(l : LH :: LT): Out = {
            val builder = cbf.newBuilder
            builder += injectOut(l.head)
            val tail = coproductOfT(l.tail)
            asTraversable(tail).foreach(elem => builder += elem.embed[Cop])
            builder.result()
          }
        }
  }

  /**
   * Type aliases and constructors provided for backward compatibility
   **/
  type ToArray[L <: HList, Lub] = ToTraversable.Aux[L, Array, Lub]
  def ToArray[L <: HList, Lub](implicit toArray: ToArray[L, Lub]) = toArray

  type ToList[L <: HList, Lub] = ToTraversable.Aux[L, List, Lub]
  def ToList[L <: HList, Lub](implicit toList: ToList[L, Lub]) = toList

  /**
   * Type class supporting conversion of this `HList` to a `Sized[M[Lub], N]` with elements typed
   * as the least upper bound Lub of the types of the elements of this `HList`.
   *
   * About serializability, see the comment in `ToTraversable`.
   *
   * @author Alexandre Archambault
   */
  trait ToSized[L <: HList, M[_]] extends DepFn1[L] with Serializable {
    type Lub
    type N <: Nat
    type Out = Sized[M[Lub], N]
    def apply(l: L): Out
  }

  object ToSized {
    def apply[L <: HList, M[_]](implicit toSized: ToSized[L, M]): Aux[L, M, toSized.Lub, toSized.N] = toSized

    type Aux[L <: HList, M[_], Lub0, N0 <: Nat] = ToSized[L, M] { type Lub = Lub0; type N = N0 }

    implicit def hnilToSized[L <: HNil, M[_], T]
      (implicit cbf : Factory[T, M[T]], ev : AdditiveCollection[M[T]]) : Aux[L, M, T, Nat._0] =
        new ToSized[L, M] {
          type Lub = T
          type N = Nat._0
          /* Calling wrap here as Sized[M]() only returns a Sized[M[Nothing], _0] */
          def apply(l : L) = Sized.wrap(cbf.newBuilder.result())
        }

    implicit def hnilToSizedNothing[L <: HNil, M[_]]
      (implicit cbf : Factory[Nothing, M[Nothing]], ev : AdditiveCollection[M[Nothing]]) : Aux[L, M, Nothing, Nat._0] =
        hnilToSized[L, M, Nothing]

    implicit def hsingleToSized[T, M[_], Lub0]
     (implicit ub : T <:< Lub0, cbf : Factory[Lub0, M[Lub0]], ev : AdditiveCollection[M[Lub0]]) : Aux[T :: HNil, M, Lub0, Nat._1] =
      new ToSized[T :: HNil, M] {
        type Lub = Lub0
        type N = Nat._1
        def apply(l : T :: HNil) = Sized[M](l.head)
      }

    implicit def hlistToSized[H1, H2, T <: HList, LT, L, N0 <: Nat, M[_]]
      (implicit
       tts  : Aux[H2 :: T, M, LT, N0],
       u    : Lub[H1, LT, L],
       tvs2 : IsIterableLike[M[LT]] { type A = LT }, // tvs2, tev, and tcbf are required for the call to map below
       tev  : AdditiveCollection[M[LT]],
       f    : Factory[L, M[L]],
       tcbf : BuildFrom[M[L], L, M[L]],
       tcbf2 : BuildFrom[M[LT], L, M[L]],
       tvs  : IsIterableLike[M[L]] { type A = L }, // tvs, cbf, and ev are required for the call to +: below
       ev   : AdditiveCollection[M[L]]) : Aux[H1 :: H2 :: T, M, L, Succ[N0]] =
        new ToSized[H1 :: H2 :: T, M] {
          type Lub = L
          type N = Succ[N0]
          def apply(l : H1 :: H2 :: T) = {
            val lhd: L = u.left(l.head)
            val tl: Sized[M[LT], N0] = tts(l.tail)
            val ltl: Sized[M[L], N0] = Sized.sizedOps(tl).map(u.right)
            Sized.sizedOps(ltl).+:(lhd)
          }
        }
  }

  /**
   * Type class supporting conversion of this `HList` to a tuple.
   *
   * @author Miles Sabin
   */
  trait Tupler[L <: HList] extends DepFn1[L] with Serializable

  object Tupler extends TuplerInstances {
    def apply[L <: HList](implicit tupler: Tupler[L]): Aux[L, tupler.Out] = tupler

    implicit val hnilTupler: Aux[HNil, Unit] =
      new Tupler[HNil] {
        type Out = Unit
        def apply(l: HNil): Out = ()
      }
  }

  /**
   * Type class supporting access to the last element of this `HList`. Available only if this `HList` has at least one
   * element.
   *
   * @author Miles Sabin
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.Last[${L}]. ${L} is empty, so there is no last element.")
  trait Last[L <: HList] extends DepFn1[L] with Serializable

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

    type Aux[L <: HList, Out0] = Last[L] { type Out = Out0 }

    implicit def hsingleLast[H]: Aux[H :: HNil, H] =
      new Last[H :: HNil] {
        type Out = H
        def apply(l : H :: HNil): Out = l.head
      }

    implicit def hlistLast[H, T <: HList, OutT]
      (implicit lt : Last.Aux[T, OutT]): Aux[H :: T, OutT] =
        new Last[H :: T] {
          type Out = OutT
          def apply(l : H :: T): Out = lt(l.tail)
        }
  }

  /**
   * Type class supporting access to all but the last element of this `HList`. Available only if this `HList` has at
   * least one element.
   *
   * @author Miles Sabin
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.Init[${L}]. {L} is empty, so there is no first element.")
  trait Init[L <: HList] extends DepFn1[L] with Serializable { type Out <: HList }

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

    type Aux[L <: HList, Out0 <: HList] = Init[L] { type Out = Out0 }

    implicit def hsingleInit[H]: Aux[H :: HNil, HNil] =
      new Init[H :: HNil] {
        type Out = HNil
        def apply(l : H :: HNil): Out = HNil
      }

    implicit def hlistInit[H, T <: HList, OutH, OutT <: HList]
      (implicit it : Init.Aux[T, OutT]): Aux[H :: T, H :: OutT] =
        new Init[H :: T] {
          type Out = H :: OutT
          def apply(l : H :: T): Out = l.head :: it(l.tail)
        }
  }

  /**
   * Type class supporting access to the first element of this `HList` of type `U`. Available only if this `HList`
   * contains an element of type `U`.
   *
   * @author Miles Sabin
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.Selector[${L}, ${U}]. You requested an element of type ${U}, but there is none in the HList ${L}.")
  trait Selector[L <: HList, U] extends DepFn1[L] with Serializable { type Out = U }

  object Selector {

    def apply[L <: HList, U](implicit selector: Selector[L, U]): Selector[L, U] = selector

    implicit def select[H, T <: HList]: Selector[H :: T, H] =
      new Selector[H :: T, H] {
        def apply(l : H :: T) = l.head
      }

    implicit def recurse[H, T <: HList, U]
      (implicit st : Selector[T, U]): Selector[H :: T, U] =
        new Selector[H :: T, U] {
          def apply(l : H :: T) = st(l.tail)
        }
  }

  /**
    * Type class supporting multiple HList field selection.
    * Can be used to witness that given HList contains certain set of field types.
    * Simplified version of shapeless.ops.record.SelectAll
    *
    * @author Ievgen Garkusha
    */
  @annotation.implicitNotFound(msg = "Field types set of ${S} is not fully contained in type definition of ${L}")
  trait SelectAll[L <: HList, S <: HList] extends DepFn1[L] with Serializable { type Out = S }

  object SelectAll {

    implicit def hnilSelectAll[L <: HList]: SelectAll[L, HNil] =
      new SelectAll[L, HNil] {
        def apply(l: L): Out = HNil
      }

    implicit def hconsSelectAll[L <: HList, H, S <: HList]
    (implicit sh: Selector[L, H], st: SelectAll[L, S]): SelectAll[L, H :: S] =
      new SelectAll[L, H :: S] {
        def apply(l: L): Out = sh(l) :: st(l)
      }
  }

  /**
   * Type class supporting access to the elements of this `HList` specified by `Ids`. Available only if this `HList`
   * contains all elements specified in `Ids`.
   *
   * @author Andreas Koestler
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.SelectMany[${L}, ${Ids}]. You requested the elements in ${Ids}, but HList ${L} does not contain all of them.")
  trait SelectMany[L <: HList, Ids <: HList] extends DepFn1[L] { type Out <: HList }

  object SelectMany {
    def apply[L <: HList, Ids <: HList](implicit sel: SelectMany[L, Ids]): Aux[L, Ids, sel.Out] = sel

    type Aux[L <: HList, Ids <: HList, Out0 <: HList] = SelectMany[L, Ids] { type Out = Out0 }

    implicit def SelectManyHNil[L <: HList]: Aux[L, HNil, HNil] =
      new SelectMany[L, HNil] {
        type Out = HNil
        def apply(l : L): Out = HNil
      }

    implicit def SelectManyHList[L <:HList, H <: Nat, T <: HList, SelOut <: HList, AtOut]
    (implicit sel : SelectMany.Aux[L, T, SelOut], at: At.Aux[L, H, AtOut]): Aux[L, H::T, AtOut :: SelOut] =
      new SelectMany[L, H::T] {
        type Out = AtOut :: SelOut
        def apply(l: L): Out = at(l) :: sel(l)
      }
  }

  /**
   * Type class supporting supporting access to the elements in range [a,b[ of this `HList`.
   * Avaialable only if this `HList` contains all elements in range
   *
   * @author Andreas Koestler
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.SelectRange[${L}, ${A}, ${B}]. You requested the elements in range [${A},${B}[, but HList ${L} does not contain all of them.")
  trait SelectRange[L <: HList, A <: Nat, B <: Nat] extends DepFn1[L] { type Out <: HList }

  object SelectRange {
    def apply[L <: HList, A <: Nat, B <: Nat](implicit sel: SelectRange[L, A, B]): Aux[L, A, B, sel.Out] = sel

    type Aux[L <: HList, A <: Nat, B <: Nat, Out0 <: HList] = SelectRange[L, A, B] {type Out = Out0}

    implicit def SelectRangeAux[L <: HList, A <: Nat, B <: Nat, Ids <: HList, SelOut <: HList]
    (implicit range: shapeless.ops.nat.Range.Aux[A, B, Ids], sel: SelectMany.Aux[L, Ids, SelOut]): Aux[L, A, B, SelOut] =
      new SelectRange[L, A, B] {
        type Out = SelOut

        def apply(l: L): Out = sel(l)
      }
  }



  /**
   * Type class supporting partitioning this `HList` into those elements of type `U` and the
   * remainder
   *
   * @author Stacy Curl
   */
  trait Partition[L <: HList, U] extends DepFn1[L] with Serializable {
    type Prefix <: HList
    type Suffix <: HList
    type Out = (Prefix, Suffix)

    def apply(l: L): Out = toTuple2(product(l))
    def product(l: L): Prefix :: Suffix :: HNil = filter(l) :: filterNot(l) :: HNil
    def filter(l: L): Prefix
    def filterNot(l: L): Suffix
  }

  object Partition {
    def apply[L <: HList, U]
      (implicit partition: Partition[L, U]): Aux[L, U, partition.Prefix, partition.Suffix] = partition

    type Aux[L <: HList, U, Prefix0 <: HList, Suffix0 <: HList] = Partition[L, U] {
      type Prefix = Prefix0
      type Suffix = Suffix0
    }

    implicit def hlistPartitionNil[U]: Aux[HNil, U, HNil, HNil] = new Partition[HNil, U] {
      type Prefix = HNil
      type Suffix = HNil

      def filter(l: HNil): HNil = HNil
      def filterNot(l: HNil): HNil = HNil
    }

    implicit def hlistPartition1[H, L <: HList, LPrefix <: HList, LSuffix <: HList](
      implicit p: Aux[L, H, LPrefix, LSuffix]
    ): Aux[H :: L, H, H :: LPrefix, LSuffix] = new Partition[H :: L, H] {
      type Prefix = H :: LPrefix
      type Suffix = LSuffix

      def filter(l: H :: L): Prefix    = l.head :: p.filter(l.tail)
      def filterNot(l: H :: L): Suffix = p.filterNot(l.tail)
    }

    implicit def hlistPartition2[H, L <: HList, U, LPrefix <: HList, LSuffix <: HList](
      implicit p: Aux[L, U, LPrefix, LSuffix], e: U =:!= H
    ): Aux[H :: L, U, LPrefix, H :: LSuffix] = new Partition[H :: L, U] {
      type Prefix = LPrefix
      type Suffix = H :: LSuffix

      def filter(l: H :: L): Prefix    = p.filter(l.tail)
      def filterNot(l: H :: L): Suffix = l.head :: p.filterNot(l.tail)
    }
  }


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

  trait LowPriorityGrouper {

    implicit def hlistGrouper[L <: HList, N <: Nat, OutT <: HList, OutD <: HList, T, Step <: Nat, LL <: Nat, M <: Nat, OutG <: HList]
    (implicit
     len: Length.Aux[L, LL],
     min: ops.nat.Min.Aux[LL, Step, M],
     take: ops.hlist.Take.Aux[L, N, OutT],
     drop: ops.hlist.Drop.Aux[L, M, OutD],
     tup: ops.hlist.Tupler.Aux[OutT, T],
     grouper: Grouper.Aux[OutD, N, Step, OutG]
    ): Grouper.Aux[L, N, Step, T :: OutG] = new Grouper[L, N, Step] {
      type Out = T :: OutG

      def apply(l: L) = tup(take(l)) :: grouper(drop(l))
    }
  }

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

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

    implicit def hnilGrouper[N <: Nat, Step <: Nat]: Aux[HNil, N, Step, HNil] = new Grouper[HNil, N, Step] {
      type Out = HNil

      def apply(l: HNil): Out = HNil
    }

    implicit def hlistGrouper1[L <: HList, A <: Nat, N <: Nat, Step <: Nat](implicit
                                                                            len: ops.hlist.Length.Aux[L, A],
                                                                            lt: ops.nat.LT[A, N]
                                                                             ): Aux[L, N, Step, HNil] = new Grouper[L, N, Step] {
      type Out = HNil

      def apply(l: L): Out = HNil
    }
  }

  /**
   * Typeclass supporting grouping this `HList` into tuples of `N` items each, at `Step`
   * apart. If `Step` equals `N` then the groups do not overlap.
   *
   * Use elements in `pad` as necessary to complete last group up to `n` items.
   *
   * @author Andreas Koestler
   */
  trait LowPriorityPaddedGrouper {

    implicit def incompletePaddedGrouper[
    L <: HList,
    N <: Nat,
    Step <: Nat,
    Pad <: HList,
    PL <: HList,
    LPL <: Nat,
    S <: Nat,
    MI <: Nat,
    T <: HList,
    M <: Nat,
    LL <: Nat,
    Out0 <: HList]
    (implicit
     len1: Length.Aux[L, LL],
     prep: Prepend.Aux[L, Pad, PL],
     len2: Length.Aux[PL, LPL],
     mod: ops.nat.Mod.Aux[LL, Step, M],
     sum: ops.nat.Sum.Aux[LL, M, S],
     min: ops.nat.Min.Aux[LPL, S, MI],
     take: Take.Aux[PL, MI, T],
     grouper: Grouper.Aux[T, N, Step, Out0]
      ): PaddedGrouper.Aux[L, N, Step, Pad, Out0] = new PaddedGrouper[L, N, Step, Pad] {

      type Out = Out0

      def apply(l: L, pad: Pad): Out = grouper(take(prep(l, pad)))
    }

  }

  trait PaddedGrouper[L <: HList, N <: Nat, Step <: Nat, Pad <: HList] extends DepFn2[L, Pad] with Serializable {
    type Out <: HList
  }

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

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

    implicit def defaultPaddedGrouper[L <: HList, N <: Nat, Step <: Nat, Pad <: HList, A <: Nat, B <: Nat, Out0 <: HList]
    (implicit
     len: Length.Aux[L, A],
     mod: ops.nat.Mod.Aux[A, Step, B],
     eq: B =:= _0,
     grouper: Grouper.Aux[L, N, Step, Out0]
      ): Aux[L, N, Step, Pad, Out0] = new PaddedGrouper[L, N, Step, Pad] {

      type Out = grouper.Out

      def apply(l: L, pad: Pad): Out = grouper(l)
    }

  }


  /**
   * Type class supporting access to the all elements of this `HList` of type `U`.
   *
   * @author Alois Cochard
   */
  trait Filter[L <: HList, U] extends DepFn1[L] with Serializable { type Out <: HList }

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

    type Aux[L <: HList, U, Out0 <: HList] = Filter[L, U] { type Out = Out0 }

    implicit def hlistFilter[L <: HList, U, LPrefix <: HList, LSuffix <: HList](
      implicit partition: Partition.Aux[L, U, LPrefix, LSuffix]
    ): Aux[L, U, LPrefix] = new Filter[L, U] {
      type Out = LPrefix

      def apply(l: L): Out = partition.filter(l)
    }
  }

  /**
   * Type class supporting access to the all elements of this `HList` of type different than `U`.
   *
   * @author Alois Cochard
   */
  trait FilterNot[L <: HList, U] extends DepFn1[L] with Serializable { type Out <: HList }

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

    type Aux[L <: HList, U, Out0 <: HList] = FilterNot[L, U] { type Out = Out0 }

    implicit def hlistFilterNot[L <: HList, U, LPrefix <: HList, LSuffix <: HList](
      implicit partition: Partition.Aux[L, U, LPrefix, LSuffix]
    ): Aux[L, U, LSuffix] = new FilterNot[L, U] {
      type Out = LSuffix

      def apply(l: L): Out = partition.filterNot(l)
    }
  }

  /**
   * Type class supporting removal of an element from this `HList`. Available only if this `HList` contains an
   * element of type `E`.
   *
   * @author Stacy Curl
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.Remove[${L}, ${E}]. You requested to remove an element of type ${E}, but there is no unique candidate in the HList ${L}.")
  trait Remove[L <: HList, E] extends DepFn1[L] with Serializable {
    def reinsert(out: Out): L
  }

  trait LowPriorityRemove {
    type Aux[L <: HList, E, Out0] = Remove[L, E] { type Out = Out0 }

    implicit def recurse[H, T <: HList, E, OutT <: HList](implicit r : Aux[T, E, (E, OutT)]): Aux[H :: T, E, (E, H :: OutT)] =
      new Remove[H :: T, E] {
        type Out = (E, H :: OutT)
        def apply(l : H :: T): Out = {
          val (e, tail) = r(l.tail)
          (e, l.head :: tail)
        }

        def reinsert(out: (E, H :: OutT)): H :: T = out._2.head :: r.reinsert((out._1, out._2.tail))
      }
  }

  object Remove extends LowPriorityRemove {
    def apply[L <: HList, E](implicit remove: Remove[L, E]): Aux[L, E, remove.Out] = remove

    implicit def remove[H, T <: HList]: Aux[H :: T, H, (H, T)] =
      new Remove[H :: T, H] {
        type Out = (H, T)
        def apply(l : H :: T): Out = (l.head, l.tail)

        def reinsert(out: (H, T)): H :: T = out._1 :: out._2
      }
  }

  /**
   * Type class supporting removal of a sublist from this `HList`. Available only if this `HList` contains a
   * sublist of type `SL`.
   *
   * The elements of `SL` do not have to be contiguous in this `HList`.
   *
   * @author Stacy Curl
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.RemoveAll[${L}, ${SL}]. You requested to remove elements of the types ${SL}, but not all were found in HList ${L}.")
  trait RemoveAll[L <: HList, SL <: HList] extends DepFn1[L] with Serializable {
    def reinsert(out: Out): L
  }

  object RemoveAll {
    def apply[L <: HList, SL <: HList](implicit remove: RemoveAll[L, SL]): Aux[L, SL, remove.Out] = remove

    type Aux[L <: HList, SL <: HList, Out0] = RemoveAll[L, SL] { type Out = Out0 }

    implicit def hlistRemoveAllNil[L <: HList]: Aux[L, HNil, (HNil, L)] =
      new RemoveAll[L, HNil] {
        type Out = (HNil, L)
        def apply(l : L): Out = (HNil, l)

        def reinsert(out: (HNil, L)): L = out._2
      }

    implicit def hlistRemoveAll[L <: HList, E, RemE <: HList, Rem <: HList, SLT <: HList]
      (implicit rt : Remove.Aux[L, E, (E, RemE)], st : Aux[RemE, SLT, (SLT, Rem)]): Aux[L, E :: SLT, (E :: SLT, Rem)] =
        new RemoveAll[L, E :: SLT] {
          type Out = (E :: SLT, Rem)
          def apply(l : L): Out = {
            val (e, rem) = rt(l)
            val (sl, left) = st(rem)
            (e :: sl, left)
          }

          def reinsert(out: (E :: SLT, Rem)): L =
            rt.reinsert((out._1.head, st.reinsert((out._1.tail, out._2))))
        }
  }

  /**
   * Type class supporting `HList` union. In case of duplicate types, this operation is a order-preserving multi-set union.
   * If type `T` appears n times in this `HList` and m > n times in `M`, the resulting `HList` contains the first n elements
   * of type `T` in this `HList`, followed by the last m - n element of type `T` in `M`.
   *
   * @author Olivier Blanvillain
   */
  trait Union[L <: HList, M <: HList] extends DepFn2[L, M] with Serializable { type Out <: HList }

  trait LowPriorityUnion {
    type Aux[L <: HList, M <: HList, Out0 <: HList] = Union[L, M] { type Out = Out0 }

    // buggy version; let (H :: T) ∪ M  =  H :: (T ∪ M)
    @deprecated("Incorrectly witnesses that {x} ∪ {x} = {x, x}", "2.3.1")
    def hlistUnion1[H, T <: HList, M <: HList, U <: HList]
      (implicit u: Union.Aux[T, M, U]): Aux[H :: T, M, H :: U] =
        new Union[H :: T, M] {
          type Out = H :: U
          def apply(l: H :: T, m: M): Out = l.head :: u(l.tail, m)
        }
  }

  object Union extends LowPriorityUnion {
    def apply[L <: HList, M <: HList](implicit union: Union[L, M]): Aux[L, M, union.Out] = union

    // let ∅ ∪ M = M
    implicit def hlistUnion[M <: HList]: Aux[HNil, M, M] =
      new Union[HNil, M] {
        type Out = M
        def apply(l: HNil, m: M): Out = m
      }

    // let (H :: T) ∪ M  =  H :: (T ∪ M) when H ∉ M
    implicit def hlistUnion1[H, T <: HList, M <: HList, U <: HList]
      (implicit
       u: Union.Aux[T, M, U],
       f: FilterNot.Aux[M, H, M]
      ): Aux[H :: T, M, H :: U] =
        new Union[H :: T, M] {
          type Out = H :: U
          def apply(l: H :: T, m: M): Out = l.head :: u(l.tail, m)
        }

    // let (H :: T) ∪ M  =  H :: (T ∪ (M - H)) when H ∈ M
    implicit def hlistUnion2[H, T <: HList, M <: HList, MR <: HList, U <: HList]
      (implicit
        r: Remove.Aux[M, H, (H, MR)],
        u: Union.Aux[T, MR, U]
      ): Aux[H :: T, M, H :: U] =
        new Union[H :: T, M] {
          type Out = H :: U
          def apply(l: H :: T, m: M): Out = l.head :: u(l.tail, r(m)._2)
        }
  }

  /**
   * Type class supporting `HList` intersection. In case of duplicate types, this operation is a multiset intersection.
   * If type `T` appears n times in this `HList` and m < n times in `M`, the resulting `HList` contains the first m
   * elements of type `T` in this `HList`.
   *
   * Also available if `M` contains types absent in this `HList`.
   *
   * @author Olivier Blanvillain
   */
  trait Intersection[L <: HList, M <: HList] extends DepFn1[L] with Serializable { type Out <: HList }

  trait LowPriorityIntersection {
    type Aux[L <: HList, M <: HList, Out0 <: HList] = Intersection[L, M] { type Out = Out0 }

    // buggy version;  let (H :: T) ∩ M  =  T ∩ M
    @deprecated("Incorrectly witnesses that {x} ∩ M = ∅", "2.3.1")
    def hlistIntersection1[H, T <: HList, M <: HList, I <: HList]
      (implicit i: Intersection.Aux[T, M, I]): Aux[H :: T, M, I] =
        new Intersection[H :: T, M] {
          type Out = I
          def apply(l: H :: T): Out = i(l.tail)
        }
  }

  object Intersection extends LowPriorityIntersection {
    def apply[L <: HList, M <: HList](implicit intersection: Intersection[L, M]): Aux[L, M, intersection.Out] = intersection

    // let ∅ ∩ M = ∅
    implicit def hnilIntersection[M <: HList]: Aux[HNil, M, HNil] =
      new Intersection[HNil, M] {
        type Out = HNil
        def apply(l: HNil): Out = HNil
      }

    // let (H :: T) ∩ M  =  T ∩ M  when H ∉ M
    implicit def hlistIntersection1[H, T <: HList, M <: HList, I <: HList]
      (implicit
       i: Intersection.Aux[T, M, I],
       f: FilterNot.Aux[M, H, M]
      ): Aux[H :: T, M, I] =
        new Intersection[H :: T, M] {
          type Out = I
          def apply(l: H :: T): Out = i(l.tail)
        }

    // let (H :: T) ∩ M  =  H :: (T ∩ (M - H)) when H ∈ M
    implicit def hlistIntersection2[H, T <: HList, M <: HList, MR <: HList, I <: HList]
      (implicit
        r: Remove.Aux[M, H, (H, MR)],
        i: Intersection.Aux[T, MR, I]
      ): Aux[H :: T, M, H :: I] =
        new Intersection[H :: T, M] {
          type Out = H :: I
          def apply(l: H :: T): Out = l.head :: i(l.tail)
        }
  }

  /**
   * Type class supporting `HList` subtraction. In case of duplicate types, this operation is a multiset difference.
   * If type `T` appears n times in this `HList` and m < n times in `M`, the resulting `HList` contains the last n - m
   * elements of type `T` in this `HList`.
   *
   * Also available if `M` contains types absent in this `HList`.
   *
   * @author Olivier Blanvillain
   */
  trait Diff[L <: HList, M <: HList] extends DepFn1[L] with Serializable { type Out <: HList }

  trait LowPriorityDiff {
    type Aux[L <: HList, M <: HList, Out0] = Diff[L, M] { type Out = Out0 }

    implicit def hconsDiff1[L <: HList, H, T <: HList, D <: HList]
      (implicit d: Diff.Aux[L, T, D]): Aux[L, H :: T, D] =
        new Diff[L, H :: T] {
          type Out = D
          def apply(l: L): Out = d(l)
        }
  }

  object Diff extends LowPriorityDiff {
    def apply[L <: HList, M <: HList](implicit diff: Diff[L, M]): Aux[L, M, diff.Out] = diff

    implicit def hnilDiff[L <: HList]: Aux[L, HNil, L] =
      new Diff[L, HNil] {
        type Out = L
        def apply(l: L): Out = l
      }

    implicit def hconsDiff2[L <: HList, LT <: HList, H, T <: HList, D <: HList]
      (implicit
        r: Remove.Aux[L, H, (H, LT)],
        d: Diff.Aux[LT, T, D]
      ): Aux[L, H :: T, D] =
        new Diff[L, H :: T] {
          type Out = D
          def apply(l: L): Out = d(r(l)._2)
        }
  }

  /**
   * Type class supporting replacement of the first element of type U from this `HList` with an element of type V.
   * Available only if this `HList` contains an element of type `U`.
   *
   * @author Miles Sabin
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.Replacer[${L}, ${U}, ${V}]. You requested to replace an element of the type ${U}, but there is none in HList ${L}.")
  trait Replacer[L <: HList, U, V] extends DepFn2[L, V] with Serializable

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

    type Aux[L <: HList, U, V, Out0] = Replacer[L, U, V] { type Out = Out0 }

    implicit def hlistReplacer1[T <: HList, U, V]: Aux[U :: T, U, V, (U, V :: T)] =
      new Replacer[U :: T, U, V] {
        type Out = (U, V :: T)
        def apply(l : U :: T, v : V): Out = (l.head, v :: l.tail)
      }

    implicit def hlistReplacer2[H, T <: HList, U, V, OutT <: HList]
      (implicit ut : Aux[T, U, V, (U, OutT)]): Aux[H :: T, U, V, (U, H :: OutT)] =
        new Replacer[H :: T, U, V] {
          type Out = (U, H :: OutT)
          def apply(l : H :: T, v : V): Out = {
            val (u, outT) = ut(l.tail, v)
            (u, l.head :: outT)
          }
        }
  }

  /**
   * Type class supporting replacement of the first element of type U from this `HList` with the result of
   * its transformation via a given function into a new element of type V.
   * Available only if this `HList` contains an element of type `U`.
   *
   * @author Jules Gosnell
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.Modifier[${L}, ${U}, ${V}]. You requested to modify an element of the type ${U}, but there is none in HList ${L}.")
  trait Modifier[L <: HList, U, V] extends DepFn2[L, U => V] with Serializable

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

    type Aux[L <: HList, U, V, Out0] = Modifier[L, U, V] { type Out = Out0 }

    implicit def hlistModify1[T <: HList, U, V]: Aux[U :: T, U, V, (U, V :: T)] =
      new Modifier[U :: T, U, V] {
        type Out = (U, V :: T)
        def apply(l : U :: T, f : U => V): Out = {val u = l.head; (u, f(u) :: l.tail)}
      }

    implicit def hlistModify2[H, T <: HList, U, V, OutT <: HList]
      (implicit ut : Aux[T, U, V, (U, OutT)]): Aux[H :: T, U, V, (U, H :: OutT)] =
        new Modifier[H :: T, U, V] {
          type Out = (U, H :: OutT)

          def apply(l : H :: T, f : U => V): Out = {
            val (u, outT) = ut(l.tail, f)
            (u, l.head :: outT)
          }
        }
  }

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

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

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

    implicit def hlistModifierAt[L <: HList, OutL <: HList, N <: Nat, F, U, V]
    (implicit
     at: At.Aux[L, N, U],
     replacer: ReplaceAt.Aux[L, N, V, (U, OutL)]
      ): Aux[L, N, U, V, (U, OutL)] =
      new ModifierAt[L, N, U, V] {
        type Out = (U, OutL)

        import syntax.std.function._

        def apply(l: L, f: U => V): Out = {
          val (u, res) = replacer(l, f(at(l)))
          (u, res)
        }
      }
  }

  /**
   * Type class supporting replacement of the Nth element of this `HList` with an element of type V. Available only if
   * this `HList` contains at least N elements.
   *
   * @author Miles Sabin
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.ReplaceAt[${L}, ${N}, ${V}]. You requested to modify an element at the position ${N}, but the HList ${L} is too short.")
  trait ReplaceAt[L <: HList, N <: Nat, V] extends DepFn2[L, V] with Serializable

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

    type Aux[L <: HList, N <: Nat, V, Out0] = ReplaceAt[L, N, V] { type Out = Out0 }

    implicit def hlistReplaceAt1[H, T <: HList, V]: Aux[H :: T, _0, V, (H, V :: T)] =
      new ReplaceAt[H :: T, _0, V] {
        type Out = (H, V :: T)
        def apply(l : H :: T, v : V): Out = (l.head, v :: l.tail)
      }

    implicit def hlistReplaceAt2[H, T <: HList, N <: Nat, U, V, Out0 <: HList]
      (implicit ut : Aux[T, N, V, (U, Out0)]): Aux[H :: T, Succ[N], V, (U, H :: Out0)] =
        new ReplaceAt[H :: T, Succ[N], V] {
          type Out = (U, H :: Out0)
          def apply(l : H :: T, v : V): Out = {
            val (u, outT) = ut(l.tail, v)
            (u, l.head :: outT)
          }
        }
  }

  /**
   * Type class supporting access to the ''nth'' element of this `HList`. Available only if this `HList` has at least
   * ''n'' elements.
   *
   * @author Miles Sabin
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.At[${L}, ${N}]. You requested to access an element at the position ${N}, but the HList ${L} is too short.")
  trait At[L <: HList, N <: Nat] extends DepFn1[L] with Serializable

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

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

    implicit def hlistAtZero[H, T <: HList]: Aux[H :: T, _0, H] =
      new At[H :: T, _0] {
        type Out = H
        def apply(l : H :: T): Out = l.head
      }

    implicit def hlistAtN[H, T <: HList, N <: Nat, AtOut]
      (implicit att : At.Aux[T, N, AtOut]): Aux[H :: T, Succ[N], AtOut] =
        new At[H :: T, Succ[N]] {
          type Out = AtOut
          def apply(l : H :: T) : Out = att(l.tail)
        }
  }

  /**
   * Type class supporting removal of the first ''n'' elements of this `HList`. Available only if this `HList` has at
   * least ''n'' elements.
   *
   * @author Miles Sabin
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.Drop[${L}, ${N}]. You requested to drop an element at the position ${N}, but the HList ${L} is too short.")
  trait Drop[L <: HList, N <: Nat] extends DepFn1[L] with Serializable { type Out <: HList }

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

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

    implicit def hlistDrop1[L <: HList]: Aux[L, _0, L] =
      new Drop[L, _0] {
        type Out = L
        def apply(l : L): Out = l
      }

    implicit def hlistDrop2[H, T <: HList, N <: Nat, Out0 <: HList]
      (implicit dt : Drop.Aux[T, N, Out0]): Aux[H :: T, Succ[N], Out0] =
        new Drop[H :: T, Succ[N]] {
          type Out = Out0
          def apply(l : H :: T): Out = dt(l.tail)
        }
  }

  /**
   * Type class supporting retrieval of the first ''n'' elements of this `HList`. Available only if this `HList` has at
   * least ''n'' elements.
   *
   * @author Miles Sabin
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.Take[${L}, ${N}]. You requested to take ${N} elements, but the HList ${L} is too short.")
  trait Take[L <: HList, N <: Nat] extends DepFn1[L] with Serializable { type Out <: HList }

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

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

    implicit def hlistTake1[L <: HList]: Aux[L, _0, HNil] =
      new Take[L, _0] {
        type Out = HNil
        def apply(l : L): Out = HNil
      }

    implicit def hlistTake2[H, T <: HList, N <: Nat, Out0 <: HList]
      (implicit tt : Take.Aux[T, N, Out0]): Aux[H :: T, Succ[N], H :: Out0] =
        new Take[H :: T, Succ[N]] {
          type Out = H :: Out0
          def apply(l : H :: T): Out = l.head :: tt(l.tail)
        }
  }


  /**
   * Type class supporting splitting this `HList` at the ''nth'' element returning the prefix and suffix as a pair.
   * Available only if this `HList` has at least ''n'' elements.
   *
   * @author Miles Sabin
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.Split[${L}, ${N}]. You requested to split at position ${N}, but the HList ${L} is too short.")
  trait Split[L <: HList, N <: Nat] extends DepFn1[L] with Serializable {
    type Prefix <: HList
    type Suffix <: HList
    type Out = (Prefix, Suffix)

    def apply(l: L): Out = toTuple2(product(l))
    def product(l: L): Prefix :: Suffix :: HNil
  }

  object Split {
    def apply[L <: HList, N <: Nat](implicit split: Split[L, N]): Aux[L, N, split.Prefix, split.Suffix] = split

    type Aux[L <: HList, N <: Nat, Prefix0 <: HList, Suffix0 <: HList] = Split[L, N] {
      type Prefix = Prefix0
      type Suffix = Suffix0
    }

    implicit def split[L <: HList, N <: Nat, P <: HList, S <: HList]
      (implicit split : Split0[HNil, L, N, P, S]): Aux[L, N, P, S] =
        new Split[L, N] {
          type Prefix = P
          type Suffix = S
          def product(l : L): Prefix :: Suffix :: HNil = split(HNil, l)
        }

    trait Split0[AccP <: HList, AccS <: HList, N <: Nat, P <: HList, S <: HList] extends Serializable {
      def apply(accP : AccP, accS : AccS) : P :: S :: HNil
    }

    object Split0 {
      implicit def hlistSplit1[P <: HList, S <: HList]: Split0[P, S, _0, P, S] =
        new Split0[P, S, _0, P, S] {
          def apply(accP : P, accS : S) : P :: S :: HNil = accP :: accS :: HNil
        }

      implicit def hlistSplit2[AccP <: HList, AccSH, AccST <: HList, N <: Nat, P <: HList, S <: HList]
        (implicit st : Split0[AccP, AccST, N, P, S]): Split0[AccP, AccSH :: AccST, Succ[N], AccSH :: P, S] =
          new Split0[AccP, AccSH :: AccST, Succ[N], AccSH :: P, S] {
            def apply(accP : AccP, accS : AccSH :: AccST) : (AccSH :: P) :: S :: HNil =
              st(accP, accS.tail) match {
                case prefix :: suffix :: HNil => (accS.head :: prefix) :: suffix :: HNil
              }
          }
    }
  }

  /**
   * Type class supporting splitting this `HList` at the ''nth'' element returning the reverse prefix and suffix as a
   * pair. Available only if this `HList` has at least ''n'' elements.
   *
   * @author Miles Sabin
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.ReverseSplit[${L}, ${N}]. You requested to split at position ${N}, but the HList ${L} is too short.")
  trait ReverseSplit[L <: HList, N <: Nat] extends DepFn1[L] with Serializable {
    type Prefix <: HList
    type Suffix <: HList
    type Out = (Prefix, Suffix)

    def apply(l: L): Out = toTuple2(product(l))
    def product(l: L): Prefix :: Suffix :: HNil
  }

  object ReverseSplit {
    def apply[L <: HList, N <: Nat]
      (implicit split: ReverseSplit[L, N]): Aux[L, N, split.Prefix, split.Suffix] = split

    type Aux[L <: HList, N <: Nat, Prefix0, Suffix0] = ReverseSplit[L, N] {
      type Prefix = Prefix0
      type Suffix = Suffix0
    }

    implicit def reverseSplit[L <: HList, N <: Nat, P <: HList, S <: HList]
      (implicit split : ReverseSplit0[HNil, L, N, P, S]): Aux[L, N, P, S] =
        new ReverseSplit[L, N] {
          type Prefix = P
          type Suffix = S
          def product(l : L): Prefix :: Suffix :: HNil = split(HNil, l)
        }

    trait ReverseSplit0[AccP <: HList, AccS <: HList, N <: Nat, P, S] extends Serializable {
      def apply(accP : AccP, accS : AccS): P :: S :: HNil
    }

    object ReverseSplit0 {
      implicit def hlistReverseSplit1[P <: HList, S <: HList]: ReverseSplit0[P, S, _0, P, S] =
        new ReverseSplit0[P, S, _0, P, S] {
          def apply(accP : P, accS : S): P :: S :: HNil = accP :: accS :: HNil
        }

      implicit def hlistReverseSplit2[AccP <: HList, AccSH, AccST <: HList, N <: Nat, P, S]
        (implicit st : ReverseSplit0[AccSH :: AccP, AccST, N, P, S]): ReverseSplit0[AccP, AccSH :: AccST, Succ[N], P, S] =
          new ReverseSplit0[AccP, AccSH :: AccST, Succ[N], P, S] {
            def apply(accP : AccP, accS : AccSH :: AccST): P :: S :: HNil = st(accS.head :: accP, accS.tail)
          }
    }
  }

  /**
   * Type class supporting splitting this `HList` at the first occurrence of an element of type `U` returning the prefix
   * and suffix as a pair. Available only if this `HList` contains an element of type `U`.
   *
   * @author Miles Sabin
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.SplitLeft[${L}, ${U}]. You requested to split at an element of type ${U}, but there is none in the HList ${L}.")
  trait SplitLeft[L <: HList, U] extends DepFn1[L] with Serializable {
    type Prefix <: HList
    type Suffix <: HList
    type Out = (Prefix, Suffix)

    def apply(l: L): Out = toTuple2(product(l))
    def product(l: L): Prefix :: Suffix :: HNil
  }

  object SplitLeft {
    def apply[L <: HList, U](implicit split: SplitLeft[L, U]): Aux[L, U, split.Prefix, split.Suffix] = split

    type Aux[L <: HList, U, Prefix0 <: HList, Suffix0 <: HList] = SplitLeft[L, U] {
      type Prefix = Prefix0
      type Suffix = Suffix0
    }

    implicit def splitLeft[L <: HList, U, P <: HList, S <: HList]
      (implicit splitLeft : SplitLeft0[HNil, L, U, P, S]): Aux[L, U, P, S] =
        new SplitLeft[L, U] {
          type Prefix = P
          type Suffix = S

          def product(l : L): Prefix :: Suffix :: HNil = splitLeft(HNil, l)
        }

    trait SplitLeft0[AccP <: HList, AccS <: HList, U, P <: HList, S <: HList] extends Serializable {
      def apply(accP : AccP, accS : AccS) : P :: S :: HNil
    }

    trait LowPrioritySplitLeft0 {
      implicit def hlistSplitLeft1[AccP <: HList, AccSH, AccST <: HList, U, P <: HList, S <: HList]
        (implicit slt : SplitLeft0[AccP, AccST, U, P, S]): SplitLeft0[AccP, AccSH :: AccST, U, AccSH :: P, S] =
          new SplitLeft0[AccP, AccSH :: AccST, U, AccSH :: P, S] {
            def apply(accP : AccP, accS : AccSH :: AccST): (AccSH :: P) :: S :: HNil =
              slt(accP, accS.tail) match {
                case prefix :: suffix :: HNil => (accS.head :: prefix) :: suffix :: HNil
              }
          }
    }

    object SplitLeft0 extends LowPrioritySplitLeft0 {
      implicit def hlistSplitLeft2[P <: HList, SH, ST <: HList]: SplitLeft0[P, SH :: ST, SH, P, SH :: ST] =
        new SplitLeft0[P, SH :: ST, SH, P, SH :: ST] {
          def apply(accP : P, accS : SH :: ST) : P :: (SH :: ST) :: HNil = accP :: accS :: HNil
        }
    }
  }

  /**
   * Type class supporting splitting this `HList` at the first occurrence of an element of type `U` returning the reverse
   * prefix and suffix as a pair. Available only if this `HList` contains an element of type `U`.
   *
   * @author Miles Sabin
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.ReverseSplitLeft[${L}, ${U}]. You requested to split at an element of type ${U}, but there is none in the HList ${L}.")
  trait ReverseSplitLeft[L <: HList, U] extends DepFn1[L] with Serializable {
    type Prefix <: HList
    type Suffix <: HList
    type Out = (Prefix, Suffix)

    def apply(l: L): Out = toTuple2(product(l))
    def product(l: L): Prefix :: Suffix :: HNil
  }

  object ReverseSplitLeft {
    def apply[L <: HList, U]
      (implicit split: ReverseSplitLeft[L, U]): Aux[L, U, split.Prefix, split.Suffix] = split

    type Aux[L <: HList, U, Prefix0 <: HList, Suffix0 <: HList] = ReverseSplitLeft[L, U] {
      type Prefix = Prefix0
      type Suffix = Suffix0
    }

    implicit def reverseSplitLeft[L <: HList, U, P <: HList, S <: HList]
      (implicit splitLeft : ReverseSplitLeft0[HNil, L, U, P, S]): Aux[L, U, P, S] =
        new ReverseSplitLeft[L, U] {
          type Prefix = P
          type Suffix = S
          def product(l : L): Prefix :: Suffix :: HNil = splitLeft(HNil, l)
        }

    trait ReverseSplitLeft0[AccP <: HList, AccS <: HList, U, P, S] extends Serializable {
      def apply(accP : AccP, accS : AccS): P :: S :: HNil
    }

    trait LowPriorityReverseSplitLeft0 {
      implicit def hlistReverseSplitLeft1[AccP <: HList, AccSH, AccST <: HList, U, P, S]
        (implicit slt : ReverseSplitLeft0[AccSH :: AccP, AccST, U, P, S]): ReverseSplitLeft0[AccP, AccSH :: AccST, U, P, S] =
          new ReverseSplitLeft0[AccP, AccSH :: AccST, U, P, S] {
            def apply(accP : AccP, accS : AccSH :: AccST): P :: S :: HNil = slt(accS.head :: accP, accS.tail)
          }
    }

    object ReverseSplitLeft0 extends LowPriorityReverseSplitLeft0 {
      implicit def hlistReverseSplitLeft2[P <: HList, SH, ST <: HList]: ReverseSplitLeft0[P, SH :: ST, SH, P, SH :: ST] =
        new ReverseSplitLeft0[P, SH :: ST, SH, P, SH :: ST] {
          def apply(accP : P, accS : SH :: ST) : P :: (SH :: ST) :: HNil = accP :: accS :: HNil
        }
    }
  }

  /**
   * Type class supporting splitting this `HList` at the last occurrence of an element of type `U` returning the prefix
   * and suffix as a pair. Available only if this `HList` contains an element of type `U`.
   *
   * @author Miles Sabin
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.SplitRight[${L}, ${U}]. You requested to split at an element of type ${U}, but there is none in the HList ${L}.")
  trait SplitRight[L <: HList, U] extends DepFn1[L] with Serializable {
    type Prefix <: HList
    type Suffix <: HList
    type Out = (Prefix, Suffix)

    def apply(l: L): Out = toTuple2(product(l))
    def product(l: L): Prefix :: Suffix :: HNil
  }

  object SplitRight {
    def apply[L <: HList, U](implicit split: SplitRight[L, U]): Aux[L, U, split.Prefix, split.Suffix] = split

    type Aux[L <: HList, U, Prefix0 <: HList, Suffix0 <: HList] = SplitRight[L, U] {
      type Prefix = Prefix0
      type Suffix = Suffix0
    }

    implicit def splitRight[L <: HList, U, P <: HList, S <: HList]
      (implicit splitRight : SplitRight0[L, HNil, HNil, U, P, S]): Aux[L, U, P, S] =
        new SplitRight[L, U] {
          type Prefix = P
          type Suffix = S
          def product(l : L): Prefix :: Suffix :: HNil = splitRight(l, HNil, HNil)
        }

    trait SplitRight0[Rev <: HList, AccP <: HList, AccS <: HList, U, P <: HList, S <: HList] extends Serializable {
      def apply(rev : Rev, accP : AccP, accS : AccS): P :: S :: HNil
    }

    trait LowPrioritySplitRight0 {
      implicit def hlistSplitRight1[RevH, RevT <: HList, AccP <: HList, U, P <: HList, S <: HList]
        (implicit srt : SplitRight0[RevT, RevH :: AccP, HNil, U, P, S]): SplitRight0[RevH :: RevT, AccP, HNil, U, P, S] =
          new SplitRight0[RevH :: RevT, AccP, HNil, U, P, S] {
            def apply(rev : RevH :: RevT, accP : AccP, accS : HNil): P :: S :: HNil = srt(rev.tail, rev.head :: accP, accS)
          }

      implicit def hlistSplitRight2[AccPH, AccPT <: HList, AccS <: HList, U, P <: HList, S <: HList]
        (implicit srt : SplitRight0[HNil, AccPT, AccPH :: AccS, U, P, S]): SplitRight0[HNil, AccPH :: AccPT, AccS, U, P, S] =
          new SplitRight0[HNil, AccPH :: AccPT, AccS, U, P, S] {
            def apply(rev : HNil, accP : AccPH :: AccPT, accS : AccS): P :: S :: HNil = srt(rev, accP.tail, accP.head :: accS)
          }
    }

    object SplitRight0 extends LowPrioritySplitRight0 {
      implicit def hlistSplitRight3[PH, PT <: HList, S <: HList, Out0 <: HList]
        (implicit reverse : Reverse.Aux[PH :: PT, Out0]): SplitRight0[HNil, PH :: PT, S, PH, Out0, S] =
          new SplitRight0[HNil, PH :: PT, S, PH, Out0, S] {
            def apply(rev : HNil, accP : PH :: PT, accS : S): Out0 :: S :: HNil = accP.reverse :: accS :: HNil
          }
    }
  }

  /**
   * Type class supporting splitting this `HList` at the last occurrence of an element of type `U` returning the reverse
   * prefix and suffix as a pair. Available only if this `HList` contains an element of type `U`.
   *
   * @author Miles Sabin
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.ReverseSplitRight[${L}, ${U}]. You requested to split at an element of type ${U}, but there is none in the HList ${L}.")
  trait ReverseSplitRight[L <: HList, U] extends DepFn1[L] with Serializable {
    type Prefix <: HList
    type Suffix <: HList
    type Out = (Prefix, Suffix)

    def apply(l: L): Out = toTuple2(product(l))
    def product(l: L): Prefix :: Suffix :: HNil
  }

  object ReverseSplitRight {
    def apply[L <: HList, U](implicit split: ReverseSplitRight[L, U]): Aux[L, U, split.Prefix, split.Suffix] = split

    type Aux[L <: HList, U, Prefix0 <: HList, Suffix0 <: HList] = ReverseSplitRight[L, U] {
      type Prefix = Prefix0
      type Suffix = Suffix0
    }

    implicit def reverseSplitRight[L <: HList, U, P <: HList, S <: HList]
      (implicit splitRight : ReverseSplitRight0[L, HNil, HNil, U, P, S]): Aux[L, U, P, S] =
        new ReverseSplitRight[L, U] {
          type Prefix = P
          type Suffix = S
          def product(l : L): Prefix :: Suffix :: HNil = splitRight(l, HNil, HNil)
        }

    trait ReverseSplitRight0[Rev <: HList, AccP <: HList, AccS <: HList, U, P, S] extends Serializable {
      def apply(rev : Rev, accP : AccP, accS : AccS): P :: S :: HNil
    }

    trait LowPriorityReverseSplitRight0 {
      implicit def hlistReverseSplitRight1[RevH, RevT <: HList, AccP <: HList, U, P <: HList, S <: HList]
        (implicit srt : ReverseSplitRight0[RevT, RevH :: AccP, HNil, U, P, S]): ReverseSplitRight0[RevH :: RevT, AccP, HNil, U, P, S] =
          new ReverseSplitRight0[RevH :: RevT, AccP, HNil, U, P, S] {
            def apply(rev : RevH :: RevT, accP : AccP, accS : HNil): P :: S :: HNil = srt(rev.tail, rev.head :: accP, accS)
          }

      implicit def hlistReverseSplitRight2[AccPH, AccPT <: HList, AccS <: HList, U, P <: HList, S <: HList]
        (implicit srt : ReverseSplitRight0[HNil, AccPT, AccPH :: AccS, U, P, S]): ReverseSplitRight0[HNil, AccPH :: AccPT, AccS, U, P, S] =
          new ReverseSplitRight0[HNil, AccPH :: AccPT, AccS, U, P, S] {
            def apply(rev : HNil, accP : AccPH :: AccPT, accS : AccS): P :: S :: HNil = srt(rev, accP.tail, accP.head :: accS)
          }
    }

    object ReverseSplitRight0 extends LowPriorityReverseSplitRight0 {
      implicit def hlistReverseSplitRight3[PH, PT <: HList, S <: HList]: ReverseSplitRight0[HNil, PH :: PT, S, PH, PH :: PT, S] =
        new ReverseSplitRight0[HNil, PH :: PT, S, PH, PH :: PT, S] {
          def apply(rev : HNil, accP : PH :: PT, accS : S): (PH :: PT) :: S :: HNil = accP :: accS :: HNil
        }
    }
  }

  /**
   * Type class supporting reversing this `HList`.
   *
   * @author Miles Sabin
   */
  trait Reverse[L <: HList] extends DepFn1[L] with Serializable { type Out <: HList }

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

    type Aux[L <: HList, Out0 <: HList] = Reverse[L] { type Out = Out0 }

    implicit def reverse[L <: HList, Out0 <: HList](implicit reverse : Reverse0[HNil, L, Out0]): Aux[L, Out0] =
      new Reverse[L] {
        type Out = Out0
        def apply(l : L) : Out = reverse(HNil, l)
      }

    trait Reverse0[Acc <: HList, L <: HList, Out <: HList] extends Serializable {
      def apply(acc : Acc, l : L) : Out
    }

    object Reverse0 {
      implicit def hnilReverse[Out <: HList]: Reverse0[Out, HNil, Out] =
        new Reverse0[Out, HNil, Out] {
          def apply(acc : Out, l : HNil) : Out = acc
        }

      implicit def hlistReverse[Acc <: HList, InH, InT <: HList, Out <: HList]
        (implicit rt : Reverse0[InH :: Acc, InT, Out]): Reverse0[Acc, InH :: InT, Out] =
          new Reverse0[Acc, InH :: InT, Out] {
            def apply(acc : Acc, l : InH :: InT) : Out = rt(l.head :: acc, l.tail)
          }
    }
  }

  /**
   * Type class supporting permuting this `HList` into the same order as another `HList` with
   * the same element types.
   *
   * @author Miles Sabin
   */
  trait Align[L <: HList, M <: HList] extends (L => M) with Serializable {
    def apply(l: L): M
  }

  object Align {
    def apply[L <: HList, M <: HList](implicit alm: Align[L, M]): Align[L, M] = alm

    implicit val hnilAlign: Align[HNil, HNil] = new Align[HNil, HNil] {
      def apply(l: HNil): HNil = l
    }

    implicit def hlistAlign[L <: HList, MH, MT <: HList, R <: HList]
      (implicit select: Remove.Aux[L, MH, (MH, R)], alignTail: Align[R, MT]): Align[L, MH :: MT] = new Align[L, MH :: MT] {
      def apply(l: L): MH :: MT = {
        val (h, t) = l.removeElem[MH]
        h :: alignTail(t)
      }
    }
  }

  /**
   * Type class supporting prepending to this `HList`.
   *
   * @author Miles Sabin
   */
  trait Prepend[P <: HList, S <: HList] extends DepFn2[P, S] with Serializable { type Out <: HList }

  trait LowestPriorityPrepend {
    type Aux[P <: HList, S <: HList, Out0 <: HList] = Prepend[P, S] { type Out = Out0 }

    implicit def hlistPrepend[PH, PT <: HList, S <: HList, PtOut <: HList]
     (implicit pt : Prepend.Aux[PT, S, PtOut]): Prepend.Aux[PH :: PT, S, PH :: PtOut] =
      new Prepend[PH :: PT, S] {
        type Out = PH :: PtOut
        def apply(prefix : PH :: PT, suffix : S): Out = prefix.head :: pt(prefix.tail, suffix)
      }
  }

  trait LowPriorityPrepend extends LowestPriorityPrepend {
    /**
     * Binary compatibility stub
     * This one is for https://github.com/milessabin/shapeless/issues/406
     */
    override type Aux[P <: HList, S <: HList, Out0 <: HList] = Prepend[P, S] { type Out = Out0 }

    implicit def hnilPrepend0[P <: HList, S <: HNil]: Aux[P, S, P] =
      new Prepend[P, S] {
        type Out = P
        def apply(prefix : P, suffix : S): P = prefix
      }
  }

  object Prepend extends LowPriorityPrepend {
    def apply[P <: HList, S <: HList](implicit prepend: Prepend[P, S]): Aux[P, S, prepend.Out] = prepend

    implicit def hnilPrepend1[P <: HNil, S <: HList]: Aux[P, S, S] =
      new Prepend[P, S] {
        type Out = S
        def apply(prefix : P, suffix : S): S = suffix
      }
  }

  /**
   * Type class supporting reverse prepending to this `HList`.
   *
   * @author Miles Sabin
   */
  trait ReversePrepend[P <: HList, S <: HList] extends DepFn2[P, S] with Serializable { type Out <: HList }

  trait LowPriorityReversePrepend {
    type Aux[P <: HList, S <: HList, Out0 <: HList] = ReversePrepend[P, S] { type Out = Out0 }

    implicit def hnilReversePrepend0[P <: HList, S <: HNil]
      (implicit rv: Reverse[P]): Aux[P, S, rv.Out] =
        new ReversePrepend[P, S] {
          type Out = rv.Out
          def apply(prefix: P, suffix: S) = prefix.reverse
        }
  }

  object ReversePrepend extends LowPriorityReversePrepend {
    def apply[P <: HList, S <: HList](implicit prepend: ReversePrepend[P, S]): Aux[P, S, prepend.Out] = prepend

    implicit def hnilReversePrepend1[P <: HNil, S <: HList]: Aux[P, S, S] =
      new ReversePrepend[P, S] {
        type Out = S
        def apply(prefix: P, suffix: S) = suffix
      }

    implicit def hlistReversePrepend[PH, PT <: HList, S <: HList]
      (implicit rpt : ReversePrepend[PT, PH :: S]): Aux[PH :: PT, S, rpt.Out] =
        new ReversePrepend[PH :: PT, S] {
          type Out = rpt.Out
          def apply(prefix : PH :: PT, suffix : S): Out = rpt(prefix.tail, prefix.head :: suffix)
        }
  }

  /**
    * Typeclass supporting repeating L-typed HLists N times.
    *
    * Repeat[Int :: String :: HNil, Nat._2] => Int :: String :: Int :: String :: HNil
    *
    * @author Jeremy Smith
    */
  trait Repeat[L <: HList, N <: Nat] {
    type Out <: HList
    def apply(l: L): Out
  }

  object Repeat {

    type Aux[L <: HList, N <: Nat, Out0 <: HList] = Repeat[L, N] { type Out = Out0 }

    implicit def base[L <: HList]: Aux[L, Nat._1, L] = new Repeat[L, Nat._1] {
      type Out = L
      def apply(l: L): L = l
    }

    implicit def succ[L <: HList, Prev <: Nat, PrevOut <: HList, P <: HList](implicit
      prev: Aux[L, Prev, PrevOut],
      prepend: Prepend.Aux[L, PrevOut, P]
    ): Aux[L, Succ[Prev], P] = new Repeat[L, Succ[Prev]] {
      type Out = P
      def apply(l: L): P = prepend(l, prev(l))
    }

  }

  /**
   * Type class supporting zipping this `HList` with an `HList` of `HList`s returning an `HList` of `HList`s with each
   * element of this `HList` prepended to the corresponding `HList` element of the argument `HList`.
   *
   * @author Miles Sabin
   */
  trait ZipOne[H <: HList, T <: HList] extends DepFn2[H, T] with Serializable { type Out <: HList }

  object ZipOne extends LowPriorityZipOne {
    implicit def zipOne0: Aux[HNil, HNil, HNil] =
      new ZipOne[HNil, HNil] {
        type Out = HNil
        def apply(h : HNil, t : HNil): Out = HNil
      }

    implicit def zipOne3[H, T <: HList]: Aux[H :: HNil, T :: HNil, (H :: T) :: HNil] =
      new ZipOne[H :: HNil, T :: HNil] {
        type Out = (H :: T) :: HNil
        def apply(h : H :: HNil, t : T :: HNil): Out = (h.head :: t.head) :: HNil
      }
  }

  trait LowPriorityZipOne {
    def apply[H <: HList, T <: HList](implicit zip: ZipOne[H, T]): Aux[H, T, zip.Out] = zip

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

    implicit def zipOne1[H <: HList]: Aux[H, HNil, HNil] =
      new ZipOne[H, HNil] {
        type Out = HNil
        def apply(h : H, t : HNil): Out = HNil
      }

    implicit def zipOne2[T <: HList]: Aux[HNil, T, HNil] =
      new ZipOne[HNil, T] {
        type Out = HNil
        def apply(h : HNil, t : T): Out = HNil
      }

    implicit def zipOne4[HH, HT <: HList, TH <: HList, TT <: HList, ZotOut <: HList]
    (implicit zot : ZipOne.Aux[HT, TT, ZotOut]): Aux[HH :: HT, TH :: TT, (HH :: TH) :: ZotOut] =
        new ZipOne[HH :: HT, TH :: TT] {
          type Out = (HH :: TH) :: ZotOut
          def apply(h : HH :: HT, t : TH :: TT): Out = (h.head :: t.head) :: zot(h.tail, t.tail)
        }
  }

  /**
   * Type class supporting transposing this `HList`.
   *
   * @author Miles Sabin
   */
  trait Transposer[L <: HList] extends DepFn1[L] with Serializable { type Out <: HList }

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

    type Aux[L <: HList, Out0 <: HList] = Transposer[L] { type Out = Out0 }

    implicit def hnilTransposer: Aux[HNil, HNil] =
      new Transposer[HNil] {
        type Out = HNil
        def apply(l : HNil): Out = l
      }

    implicit def hlistTransposer1[H <: HList, MC <: HList, Out0 <: HList]
      (implicit mc : ConstMapper.Aux[HNil, H, MC], zo : ZipOne.Aux[H, MC, Out0]): Aux[H :: HNil, Out0] =
        new Transposer[H :: HNil] {
          type Out = Out0
          def apply(l : H :: HNil): Out = zo(l.head, mc(HNil, l.head))
        }

    implicit def hlistTransposer2[H <: HList, TH <: HList, TT <: HList, OutT <: HList, Out0 <: HList]
      (implicit tt : Aux[TH :: TT, OutT], zo : ZipOne.Aux[H, OutT, Out0]): Aux[H :: TH :: TT, Out0] =
        new Transposer[H :: TH :: TT] {
          type Out = Out0
          def apply(l : H :: TH :: TT): Out = zo(l.head, tt(l.tail))
        }
  }

  /**
   * Type class supporting zipping this `HList` of `HList`s returning an `HList` of tuples.
   *
   * @author Miles Sabin
   */
  trait Zip[L <: HList] extends DepFn1[L] with Serializable { type Out <: HList }

  object Zip {
    def apply[L <: HList](implicit zip: Zip[L]): Aux[L, zip.Out] = zip

    type Aux[L <: HList, Out0 <: HList] = Zip[L] { type Out = Out0 }

    implicit def zipper[L <: HList, OutT <: HList, OutM <: HList]
      (implicit
        transposer : Transposer.Aux[L, OutT],
        mapper : Mapper.Aux[tupled.type, OutT, OutM]): Aux[L, OutM] =
        new Zip[L] {
          type Out = OutM
          def apply(l : L): Out = l.transpose map tupled
        }
  }

  /**
   * Type class supporting unzipping this `HList` of tuples returning a tuple of `HList`s.
   *
   * @author Miles Sabin
   */
  trait Unzip[L <: HList] extends DepFn1[L] with Serializable

  object Unzip {
    def apply[L <: HList](implicit unzip: Unzip[L]): Aux[L, unzip.Out] = unzip

    type Aux[L <: HList, Out0] = Unzip[L] { type Out = Out0 }

    implicit def unzipper[L <: HList, OutM <: HList, TransposerOut <: HList, TuplerOut]
      (implicit
        mapper : Mapper.Aux[productElements.type, L, OutM],
        transposer : Transposer.Aux[OutM, TransposerOut],
        tupler : Tupler.Aux[TransposerOut, TuplerOut]): Aux[L, TuplerOut] =
        new Unzip[L] {
          type Out = TuplerOut
          def apply(l : L): Out = (l map productElements).transpose.tupled
        }
  }

  /**
   * Type class supporting zipping this `HList` of monomorphic function values with its argument `HList` of
   * correspondingly typed function arguments returning the result of each application as an `HList`. Available only if
   * there is evidence that the corresponding function and argument elements have compatible types.
   *
   * @author Miles Sabin
   */
  @implicitNotFound("Implicit not found: shapeless.Ops.ZipApply[${FL}, ${AL}]. The types of ${FL} and ${AL} are not compatible.")
  trait ZipApply[FL <: HList, AL <: HList] extends DepFn2[FL, AL] with Serializable { type Out <: HList }

  object ZipApply {
    def apply[FL <: HList, AL <: HList](implicit zip: ZipApply[FL, AL]): Aux[FL, AL, zip.Out] = zip

    type Aux[FL <: HList, AL <: HList, Out0 <: HList] = ZipApply[FL, AL] { type Out = Out0 }

    implicit def hnilZipApply: Aux[HNil, HNil, HNil] =
      new ZipApply[HNil, HNil] {
        type Out = HNil
        def apply(fl : HNil, al : HNil): Out = HNil
      }

    implicit def hconsZipApply[T, R, FLT <: HList, ALT <: HList, ZttOut <: HList]
      (implicit ztt : ZipApply.Aux[FLT, ALT, ZttOut]): Aux[(T => R) :: FLT, T :: ALT, R :: ZttOut] =
        new ZipApply[(T => R) :: FLT, T :: ALT] {
          type Out = R :: ZttOut
          def apply(fl : (T => R) :: FLT, al : T :: ALT): Out = fl.head(al.head) :: ztt(fl.tail, al.tail)
        }
  }

  /**
   * Type class supporting zipping an `HList` with a constant, resulting in an `HList` of tuples of the form
   * ({element from input `HList`}, {supplied constant})
   *
   * @author Cody Allen
   */
  trait ZipConst[C, L <: HList] extends DepFn2[C, L] with Serializable { type Out <: HList }

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

    type Aux[C, L <: HList, Out0 <: HList] = ZipConst[C, L] { type Out = Out0 }

    implicit def hnilZipConst[C, L <: HNil] : Aux[C, L, HNil] = new ZipConst[C, L] {
      type Out = HNil
      def apply(t: C, u: L) = HNil
    }

    implicit def constZipper[C, L <: HList, M <: HList, ZipperOut <: HList]
      (implicit
        mapper: ConstMapper.Aux[C, L, M],
        zipper: Zip.Aux[L :: M :: HNil, ZipperOut]): Aux[C, L, ZipperOut] =
        new ZipConst[C, L] {
          type Out = ZipperOut
          def apply(c: C, l: L) = zipper(l :: mapper(c, l) :: HNil)
        }
  }

  /**
   * Type class supporting zipping an 'HList' with another 'HList' using a 'Poly2' resulting in an HList
   *
   * @author Stacy Curl
   */
  trait ZipWith[L <: HList, R <: HList, P <: Poly2] extends DepFn2[L, R] with Serializable { type Out <: HList }

  object ZipWith {
    def apply[L <: HList, R <: HList, P <: Poly2]
      (implicit zipWith: ZipWith[L, R, P]): Aux[L, R, P, zipWith.Out] = zipWith

    type Aux[L <: HList, R <: HList, P <: Poly2, Out0 <: HList] = ZipWith[L, R, P] { type Out = Out0 }

    implicit def hnilZipWithHNil[P <: Poly2]: Aux[HNil, HNil, P, HNil] = constZipWith[HNil, HNil, P]
    implicit def hnilZipWithHList[R <: HList, P <: Poly2]: Aux[HNil, R, P, HNil] = constZipWith[HNil, R, P]
    implicit def hlistZipWithHNil[L <: HList, P <: Poly2]: Aux[L, HNil, P, HNil] = constZipWith[L, HNil, P]

    implicit def hlistZipWithHList[LH, RH, LT <: HList, RT <: HList, P <: Poly2, ZipWithOut <: HList, ClrResult]
      (implicit zipWith: ZipWith.Aux[LT, RT, P, ZipWithOut], clr: Case2.Aux[P, LH, RH, ClrResult])
        : Aux[LH :: LT, RH :: RT, P, ClrResult :: ZipWithOut] =
          new ZipWith[LH :: LT, RH :: RT, P] {
            type Out = ClrResult :: ZipWithOut
            def apply(l: LH :: LT, r: RH :: RT): Out =
              clr(l.head, r.head) :: zipWith(l.tail, r.tail)
          }

    private def constZipWith[L <: HList, R <: HList, P <: Poly2]: Aux[L, R, P, HNil] =
      new ZipWith[L, R, P] {
        type Out = HNil
        def apply(l: L, r: R): HNil = HNil
      }
  }

  /**
   * Type class supporting zipping an `HList` of values with an `HList` of keys to create a record.
   *
   * @author Cody Allen
   */
  trait ZipWithKeys[K <: HList, V <: HList] extends DepFn1[V] with Serializable { type Out <: HList }

  object ZipWithKeys {
    import shapeless.labelled._

    def apply[K <: HList, V <: HList]
      (implicit zipWithKeys: ZipWithKeys[K, V]): Aux[K, V, zipWithKeys.Out] = zipWithKeys

    type Aux[K <: HList, V <: HList, Out0 <: HList] = ZipWithKeys[K, V] { type Out = Out0 }

    implicit val hnilZipWithKeys: Aux[HNil, HNil, HNil] = new ZipWithKeys[HNil, HNil] {
      type Out = HNil
      def apply(v: HNil) = HNil
    }

    implicit def hconsZipWithKeys[KH, VH, KT <: HList, VT <: HList, ZwkOut <: HList] (implicit zipWithKeys: ZipWithKeys.Aux[KT, VT, ZwkOut], wkh: Witness.Aux[KH])
        : Aux[KH :: KT, VH :: VT, FieldType[KH, VH] :: ZwkOut] =
          new ZipWithKeys[KH :: KT, VH :: VT] {
            type Out = FieldType[KH, VH] :: ZwkOut
            def apply(v: VH :: VT): Out =
              field[wkh.T](v.head) :: zipWithKeys(v.tail)
          }
  }

  /**
   * Type class supporting zipping a `HList` with its element indices,  resulting in a 'HList' of  tuples of the form
   * ({element from input tuple}, {element index})
   *
   * @author Andreas Koestler
   * @author Alexandre Archambault
   */
  trait ZipWithIndex[L <: HList] extends DepFn1[L] with Serializable {
    type Out <: HList
  }

  object ZipWithIndex {
    def apply[L <: HList](implicit zipWithIndex: ZipWithIndex[L]): Aux[L, zipWithIndex.Out] =
      zipWithIndex

    type Aux[L <: HList, Out0 <: HList] = ZipWithIndex[L] { type Out = Out0 }


    trait Helper[L <: HList, N <: Nat] extends DepFn2[L, N] with Serializable {
      type Out <: HList
    }

    object Helper {
      def apply[L <: HList, N <: Nat](implicit helper: Helper[L, N]): Aux[L, N, helper.Out] = helper

      type Aux[L <: HList, N <: Nat, Out0 <: HList] = Helper[L, N] { type Out = Out0 }

      implicit def hnil[N <: Nat]: Aux[HNil, N, HNil] =
        new Helper[HNil, N] {
          type Out = HNil
          def apply(l: HNil, n: N) = HNil
        }

      implicit def hcons[H, T <: HList, OutT <: HList, N <: Nat]
       (implicit
         tail: Helper.Aux[T, Succ[N], OutT]
       ): Aux[H :: T, N, (H, N) :: OutT] =
        new Helper[H :: T, N] {
          type Out = (H, N) :: OutT
          def apply(l: H :: T, n: N) = (l.head -> n) :: tail(l.tail, Succ[N]())
        }
    }


    implicit def default[L <: HList, P <: HList]
     (implicit helper: Helper.Aux[L, _0, P]): Aux[L, P] =
      new ZipWithIndex[L] {
        type Out = P
        def apply(l: L) = helper(l, Nat._0)
      }
  }

  /**
   * Type Class witnessing that an 'HList' can be collected with a 'Poly' to produce an 'HList'
   *
   * @author Stacy Curl
   */
  trait Collect[I <: HList, P <: Poly] extends DepFn1[I] with Serializable { type Out <: HList }

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

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

    implicit def hnilCollect[P <: Poly]: Aux[HNil, P, HNil] = new Collect[HNil, P] {
      type Out = HNil

      def apply(l: HNil): Out = HNil
    }

    implicit def hlistCollect[LH, LT <: HList, P <: Poly, CollectOut <: HList, ClrResult]
      (implicit collect: Collect.Aux[LT, P, CollectOut], clr: Case1.Aux[P, LH, ClrResult])
        : Aux[LH :: LT, P, ClrResult :: CollectOut] =
          new Collect[LH :: LT, P] {
            type Out = ClrResult :: CollectOut

            def apply(l: LH :: LT): Out = clr(l.head) :: collect(l.tail)
          }
  }

  trait LowPriorityCollect {
    implicit def hlistNoPolyCase[LH, LT <: HList, P <: Poly, CollectOut <: HList]
      (implicit collect: Collect.Aux[LT, P, CollectOut]): Collect.Aux[LH :: LT, P, CollectOut] =
        new Collect[LH :: LT, P] {
          type Out = CollectOut

          def apply(l: LH :: LT): Out = collect(l.tail)
        }
  }

  implicit object hnilOrdering extends Ordering[HNil] {
    def compare(x: HNil, y: HNil): Int = 0
  }

  implicit def hlistOrdering[H, T <: HList]
    (implicit hOrdering: Ordering[H], tOrdering: Ordering[T]): Ordering[H :: T] =
      new Ordering[H :: T] {
        def compare(x: H :: T, y: H :: T): Int = {
          val compareH = hOrdering.compare(x.head, y.head)

          if (compareH != 0) compareH else tOrdering.compare(x.tail, y.tail)
        }
      }

  /*
   * Type class supporting consing an element onto each row of this HMatrix (HList of HLists)
   *
   * @author Stacy Curl
   */
  trait MapCons[A, M <: HList] extends DepFn2[A, M] with Serializable { type Out <: HList }

  object MapCons {
    def apply[A, M <: HList](implicit mapCons: MapCons[A, M]): Aux[A, M, mapCons.Out] = mapCons

    type Aux[A, M <: HList, Out0 <: HList] = MapCons[A, M] { type Out = Out0 }

    implicit def hnilMapCons[A]: Aux[A, HNil, HNil] =
      new MapCons[A, HNil] {
        type Out = HNil

        def apply(a: A, m: HNil): Out = HNil
      }

    implicit def hlistMapCons[A, H <: HList, TM <: HList, MapConsOut <: HList]
      (implicit mapCons: MapCons.Aux[A, TM, MapConsOut]): Aux[A, H :: TM, (A :: H) :: MapConsOut] =
        new MapCons[A, H :: TM] {
          type Out = (A :: H) :: MapConsOut

          def apply(a: A, l: H :: TM): Out = (a :: l.head) :: mapCons(a, l.tail)
        }
  }

  /**
   * Type class supporting adding an element to each possible position in this HList
   *
   * @author Stacy Curl
   */
  trait Interleave[A, L <: HList] extends DepFn2[A, L] with Serializable { type Out <: HList }

  object Interleave {
    def apply[A , L <: HList](implicit interleave: Interleave[A, L]): Aux[A, L, interleave.Out] = interleave

    type Aux[A, L <: HList, Out0 <: HList] = Interleave[A, L] { type Out = Out0 }

    implicit def hnilInterleave[A, L <: HNil]: Aux[A, L, (A :: HNil) :: HNil] =
      new Interleave[A, L] {
        type Out = (A :: HNil) :: HNil

        def apply(a: A, l: L): Out = (a :: HNil) :: HNil
      }

    implicit def hlistInterleave[A, H, T <: HList, LI <: HList, MapConsOut <: HList]
      (implicit interleave: Interleave.Aux[A, T, LI], mapCons: MapCons.Aux[H, LI, MapConsOut])
        : Aux[A, H :: T, (A :: H :: T) :: MapConsOut] = new Interleave[A, H :: T] {
          type Out = (A :: H :: T) :: MapConsOut

          def apply(a: A, l: H :: T): Out = (a :: l) :: mapCons(l.head, interleave(a, l.tail))
        }
  }

  /**
   * Type class supporting interleaving an element into each row of this HMatrix (HList of HLists)
   *
   * @author Stacy Curl
   */
  trait FlatMapInterleave[A, M <: HList] extends DepFn2[A, M] with Serializable { type Out <: HList }

  object FlatMapInterleave {
    def apply[A, M <: HList]
      (implicit flatMapInterleave: FlatMapInterleave[A, M]): Aux[A, M, flatMapInterleave.Out] = flatMapInterleave

    type Aux[A, M <: HList, Out0 <: HList] = FlatMapInterleave[A, M] { type Out = Out0 }

    implicit def hnilFlatMapInterleave[A, M <: HNil]: Aux[A, M, HNil]
      = new FlatMapInterleave[A, M] {
        type Out = HNil

        def apply(a: A, m: M): Out = HNil
      }

    implicit def hlistFlatMapInterleave[A, H <: HList, TM <: HList, HO <: HList, TMO <: HList, PrependOut <: HList]
      (implicit interleave: Interleave.Aux[A, H, HO],
         flatMapInterleave: FlatMapInterleave.Aux[A, TM, TMO],
         prepend: Prepend.Aux[HO, TMO, PrependOut]
       ): Aux[A, H :: TM, PrependOut] =
          new FlatMapInterleave[A, H :: TM] {
            type Out = PrependOut

            def apply(a: A, m: H :: TM): Out =
              prepend(interleave(a, m.head), flatMapInterleave(a, m.tail))
          }
  }

  /**
   * Type class supporting the calculation of every permutation of this 'HList'
   *
   * @author Stacy Curl
   */
  trait Permutations[L <: HList] extends DepFn1[L] with Serializable { type Out <: HList }

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

    type Aux[L <: HList, Out0] = Permutations[L] { type Out = Out0 }

    implicit def hnilPermutations[L <: HNil]: Aux[L, HNil :: HNil] = new Permutations[L] {
      type Out = HNil :: HNil

      def apply(l: L): Out = HNil :: HNil
    }

    implicit def hlistPermutations[H, T <: HList, TP <: HList, FlatmapInterleaveOut <: HList]
      (implicit permutations: Permutations.Aux[T, TP], flatMapInterleave: FlatMapInterleave.Aux[H, TP, FlatmapInterleaveOut])
        : Aux[H :: T, FlatmapInterleaveOut] =
          new Permutations[H :: T] {
            type Out = FlatmapInterleaveOut

            def apply(l: H :: T): Out = flatMapInterleave(l.head, permutations(l.tail))
          }
  }

  /**
   * Type class supporting rotating a HList left
   *
   * @author Stacy Curl
   */
  trait RotateLeft[L <: HList, N <: Nat] extends DepFn1[L] with Serializable { type Out <: HList }

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

    implicit def hnilRotateLeft[L <: HNil, N <: Nat]: RotateLeft.Aux[L, N, L] = new RotateLeft[L, N] {
      type Out = L
      def apply(l: L) = l
    }
  }

  trait LowPriorityRotateLeft {
    type Aux[L <: HList, N <: Nat, Out0] = RotateLeft[L, N] { type Out = Out0 }

    implicit def hlistRotateLeft[
    L <: HList, N <: Nat, Size <: Nat, NModSize <: Nat, Before <: HList, After <: HList, PrependOut <: HList
    ](implicit
      length: Length.Aux[L, Size],
      mod: nat.Mod.Aux[N, Size, NModSize],
      split: Split.Aux[L, NModSize, Before, After],
      prepend: Prepend.Aux[After, Before, PrependOut]
    ): RotateLeft.Aux[L, N, PrependOut] = new RotateLeft[L, N] {
      type Out = PrependOut

      def apply(l: L): Out = {
        val (before, after) = split(l)

        prepend(after, before)
      }
    }

    /** Binary compatibility stub */
    def noopRotateLeft[L <: HList, N <: Nat]: Aux[L, N, L] = new RotateLeft[L, N] {
      type Out = L
      def apply(l: L): Out = l
    }
  }

  /**
   * Type class supporting rotating a HList right
   *
   * @author Stacy Curl
   */
  trait RotateRight[L <: HList, N <: Nat] extends DepFn1[L] with Serializable { type Out <: HList }

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

    implicit def hnilRotateRight[L <: HNil, N <: Nat]: RotateRight.Aux[L, N, L] = new RotateRight[L, N] {
      type Out = L
      def apply(l: L) = l
    }
  }

  trait LowPriorityRotateRight {
    type Aux[L <: HList, N <: Nat, Out0 <: HList] = RotateRight[L, N] { type Out = Out0 }

    implicit def hlistRotateRight[
    L <: HList, N <: Nat, Size <: Nat, NModSize <: Nat, Size_Diff_NModSize <: Nat, RotateLeftOut <: HList
    ](implicit
      length: Length.Aux[L, Size],
      mod: nat.Mod.Aux[N, Size, NModSize],
      diff: nat.Diff.Aux[Size, NModSize, Size_Diff_NModSize],
      rotateLeft: RotateLeft.Aux[L, Size_Diff_NModSize, RotateLeftOut]
       ): RotateRight.Aux[L, N, RotateLeftOut] = new RotateRight[L, N] {
      type Out = RotateLeftOut

      def apply(l: L): Out = rotateLeft(l)
    }

    def noopRotateRight[L <: HList, N <: Nat]: Aux[L, N, L] = new RotateRight[L, N] {
      type Out = L
      def apply(l: L): Out = l
    }
  }

  /**
   * Type class supporting left scanning of this `HList` with a binary polymorphic function.
   *
   * @author Owein Reese
   */
  trait LeftScanner[L <: HList, In, P <: Poly] extends DepFn2[L, In] with Serializable {
    type Out <: HList
  }

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

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

    implicit def hnilLeftScanner[In, P <: Poly]: Aux[HNil, In, P, In :: HNil] =
      new LeftScanner[HNil, In, P]{
        type Out = In :: HNil

        def apply(l: HNil, in: In) = in :: HNil
      }

    implicit def hlistLeftScanner[H, T <: HList, In, P <: Poly, OutP, ScanOut <: HList]
      (implicit ev: Case2.Aux[P, H, In, OutP], scan: LeftScanner.Aux[T, OutP, P, ScanOut]): Aux[H :: T, In, P, In :: ScanOut] =
        new LeftScanner[H :: T, In, P]{
          type Out = In :: ScanOut

          def apply(l: H :: T, in: In) = in :: scan(l.tail, ev(l.head, in)) // check it's (h, in) vs (in, h)
        }
  }

  /**
   * Type class supporting right scanning of this `HList` with a binary polymorphic function.
   *
   * @author Owein Reese
   */
  trait RightScanner[L <: HList, In, P <: Poly] extends DepFn2[L, In] with Serializable {
    type Out <: HList
  }

  object RightScanner{
    def apply[L <: HList, In, P <: Poly](implicit scanR: RightScanner[L, In, P]) = scanR

    trait RightScanner0[L <: HList, V, P <: Poly] extends DepFn2[L, V] with Serializable {
      type Out <: HList
    }

    object RightScanner0 {
      type Aux[L <: HList, V, P <: Poly, Out0 <: HList] = RightScanner0[L, V, P] { type Out = Out0 }
    }

    implicit def hlistRightScanner0[H, H0, T <: HList, P <: Poly, C2Result](implicit ev: Case2.Aux[P, H0, H, C2Result]) =
      new RightScanner0[H :: T, H0, P]{
        type Out = C2Result :: H :: T

        def apply(l: H :: T, h: H0) = ev(h, l.head) :: l
      }

    implicit def hnilRightScanner[In, P <: Poly]: Aux[HNil, In, P, In :: HNil] =
      new RightScanner[HNil, In, P]{
        type Out = In :: HNil

        def apply(l: HNil, in: In): Out = in :: HNil
      }

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

    implicit def hlistRightScanner[H, T <: HList, In, P <: Poly, R <: HList, Scan0Out <: HList]
      (implicit scanR: Aux[T, In, P, R], scan0: RightScanner0.Aux[R, H, P, Scan0Out]): RightScanner.Aux[H :: T, In, P, Scan0Out] =
        new RightScanner[H :: T, In, P]{
          type Out = Scan0Out

          def apply(l: H :: T, in: In) = scan0(scanR(l.tail, in), l.head)
        }
  }

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

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

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

    implicit def fill1Zero[A]: Aux[Nat._0, A, HNil] =
      new Fill[Nat._0, A] {
        type Out = HNil
        def apply(elem: A) = HNil
      }

    implicit def fill1Succ[N <: Nat, A, OutT <: HList]
      (implicit prev: Aux[N, A, OutT]): Aux[Succ[N], A, A :: OutT] =
        new Fill[Succ[N], A] {
          type Out = A :: OutT
          def apply(elem: A) = elem :: prev(elem)
        }

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

  /**
    * Type class supporting producing a [[HList]] filled from a [[Poly0]].
    *
    * @author 杨博 (Yang Bo)
    */
  trait FillWith[F, L <: HList] extends DepFn0 with Serializable {
    type Out = L
  }

  object FillWith {
    def apply[F, L <: HList](implicit fill: FillWith[F, L]): FillWith[F, L] = fill

    implicit def hnilFill[F]: FillWith[F, HNil] =
      new FillWith[F, HNil] {
        def apply(): Out = HNil
      }

    implicit def hconsFill[F <: Poly, Head, Tail <: HList]
    (implicit hc: Case0.Aux[F, Head], mt: FillWith[F, Tail]): FillWith[F, Head :: Tail] =
      new FillWith[F, Head :: Tail] {
        def apply(): Out = hc() :: mt()
      }
  }

  /**
   * Type class supporting the patching of an `HList`
   *
   * @author Owein Reese
   */
  trait Patcher[N <: Nat, M <: Nat, L <: HList, In <: HList] extends DepFn2[L, In] with Serializable {
    type Out <: HList
  }

  object Patcher {
    def apply[N <: Nat, M <: Nat, L <: HList, In <: HList](implicit patch: Patcher[N, M, L, In]) = patch

    type Aux[N <: Nat, M <: Nat, L <: HList, In <: HList, Out0 <: HList] = Patcher[N, M, L, In]{ type Out = Out0 }

    implicit def hlistPatch1[N <: Nat, M <: Nat, H, T <: HList, In <: HList, PatchOut <: HList]
      (implicit patch: Patcher.Aux[N, M, T, In, PatchOut]): Aux[Succ[N], M, H :: T, In, H :: PatchOut] =
        new Patcher[Succ[N], M, H :: T, In]{
          type Out = H :: PatchOut

          def apply(l: H :: T, in: In) = l.head :: patch(l.tail, in)
        }

    implicit def hlistPatch2[M <: Nat, L <: HList, In <: HList, OutL <: HList, OutP <: HList]
      (implicit drop: Drop.Aux[L, M, OutL], prepend: Prepend.Aux[In, OutL, OutP]) =
        new Patcher[_0, M, L, In]{
          type Out = OutP

          def apply(l: L, in: In) = prepend(in, drop(l))
        }
  }

  /**
   * Type class witnessing that there exists at least one element of an `HList` for which a `Poly` can be
   * applied.
   *
   * @author Owein Reese
   */
  trait CollectFirst[L <: HList, P <: Poly] extends DepFn1[L] with Serializable

  object CollectFirst extends LowPriorityCollectFirst{
    def apply[L <: HList, P <: Poly](implicit cf: CollectFirst[L, P]): Aux[L, P, cf.Out] = cf

    implicit def hlistEval[H, T <: HList, P <: Poly](implicit ev: Case1[P, H]): Aux[H :: T, P, ev.Result] =
      new CollectFirst[H :: T, P]{
        type Out = ev.Result

        def apply(l: H :: T) = ev(l.head)
      }
  }

  trait LowPriorityCollectFirst{
    type Aux[L <: HList, P <: Poly, Out0] = CollectFirst[L, P]{ type Out = Out0 }

    implicit def hlistIterate[H, T <: HList, P <: Poly, CfOut](implicit cf: CollectFirst.Aux[T, P, CfOut]): Aux[H :: T, P, CfOut] =
      new CollectFirst[H :: T, P]{
        type Out = CfOut

        def apply(l: H :: T) = cf(l.tail)
      }
  }

  private def toTuple2[Prefix, Suffix](l: Prefix :: Suffix :: HNil): (Prefix, Suffix) = (l.head, l.tail.head)


  /**
   * Typeclass witnessing that all the elements of an HList have instances of the given typeclass.
   * Courtesy of mpilquist.
   *
   * @author Tin Pavlinic
  */
  sealed trait LiftAll[F[_], In <: HList] {
    type Out <: HList
    def instances: Out
  }

  object LiftAll {
    type Aux[F[_], In0 <: HList, Out0 <: HList] = LiftAll[F, In0] {type Out = Out0}
    class Curried[F[_]] {def apply[In <: HList](in: In)(implicit ev: LiftAll[F, In]) = ev}

    def apply[F[_]] = new Curried[F]
    def apply[F[_], In <: HList](implicit ev: LiftAll[F, In]) = ev

    implicit def hnil[F[_]]: LiftAll.Aux[F, HNil, HNil] = new LiftAll[F, HNil] {
      type Out = HNil
      def instances = HNil
    }

    implicit def hcons[F[_], H, T <: HList, TI <: HList]
      (implicit headInstance: F[H], tailInstances: Aux[F, T, TI]): Aux[F, H :: T, F[H] :: TI] =
        new LiftAll[F, H :: T] {
          type Out = F[H] :: TI
          def instances = headInstance :: tailInstances.instances
    }
  }

  /**
   * Type class supporting producing a HList of shape `N` padded with elements of type `A`.
   *
   * @author ryoppy
   */
  trait PadTo[N, A, L <: HList] extends DepFn2[A, L] with Serializable { type Out <: HList }

  object PadTo {
    def apply[N, A, L <: HList](implicit padTo: PadTo[N, A, L]): Aux[N, A, L, padTo.Out] = padTo

    type Aux[N, A, L <: HList, Out0] = PadTo[N, A, L] { type Out = Out0 }

    implicit def padToHNil0[A]: Aux[_0, A, HNil, HNil] =
      new PadTo[_0, A, HNil] {
        type Out = HNil
        def apply(a: A, l: HNil) = l
      }

    implicit def padToHNil[N <: Nat, A, Out0 <: HList](implicit padTo: PadTo.Aux[N, A, HNil, Out0]): Aux[Succ[N], A, HNil, A :: Out0] =
      new PadTo[Succ[N], A, HNil] {
        type Out = A :: Out0
        def apply(a: A, l: HNil) = a :: padTo(a, l)
      }

    implicit def padTo1[N <: Nat, A, H, T <: HList, Out0 <: HList](implicit padTo: PadTo.Aux[N, A, T, Out0]): Aux[Succ[N], A, H :: T, H :: Out0] =
      new PadTo[Succ[N], A, H :: T] {
        type Out = H :: Out0
        def apply(a: A, l: H :: T) = l.head :: padTo(a, l.tail)
      }
  }

  /**
   * Type class supporting the slicing of an `HList`
   *
   * @author ryoppy
   */
  trait Slice[N, U, L <: HList] extends DepFn1[L] with Serializable { type Out <: HList }

  object Slice {
    def apply[N, U, L <: HList](implicit slice: Slice[N, U, L]): Aux[N, U, L, slice.Out] = slice

    type Aux[N, U, L <: HList, Out0] = Slice[N, U, L] { type Out = Out0 }

    implicit def slice0[L <: HList]: Aux[_0, _0, L, HNil] =
      new Slice[_0, _0, L] {
        type Out = HNil
        def apply(l: L) = HNil
      }

    implicit def slice1[N <: Nat, U <: Nat, H, T <: HList, Out0 <: HList]
      (implicit slice: Slice.Aux[N, U, T, Out0]): Aux[Succ[N], Succ[U], H :: T, Out0] =
        new Slice[Succ[N], Succ[U], H :: T] {
          type Out = Out0
          def apply(l: H :: T): Out = slice(l.tail)
        }

    implicit def slice2[U <: Nat, H, T <: HList, Out0 <: HList]
      (implicit slice: Slice.Aux[_0, U, T, Out0]): Aux[_0, Succ[U], H :: T, H :: Out0] =
        new Slice[_0, Succ[U], H :: T] {
          type Out = H :: slice.Out
          def apply(l: H :: T): Out = l.head :: slice(l.tail)
        }
  }

  /**
    * Type class supporting reifying an `HList` of singleton types.
    *
    * @author Jisoo Park
    */
  trait Reify[L <: HList] extends DepFn0 with Serializable { type Out <: HList }

  object Reify {
    def apply[L <: HList](implicit reify: Reify[L]): Aux[L, reify.Out] = reify

    type Aux[L <: HList, Out0 <: HList] = Reify[L] { type Out = Out0 }

    implicit def hnilReify[L <: HNil]: Aux[L, HNil] =
      new Reify[L] {
        type Out = HNil
        def apply(): Out = HNil
      }

    implicit def hlistReify[H, T <: HList, Out0 <: HList](implicit
      wh: Witness.Aux[H],
      rt: Reify.Aux[T, Out0]
    ) : Aux[H :: T, H :: Out0] =
      new Reify[H :: T] {
        type Out = H :: rt.Out
        def apply(): Out = wh.value :: rt()
      }
  }

  /**
   * Type class supporting the calculation of every combination of this 'HList'
   *
   * @author ryoppy
   */
  trait Combinations[N <: Nat, L <: HList] extends DepFn1[L] with Serializable {
    type Out <: HList
  }

  object Combinations extends LowPriorityCombinations {
    def apply[L <: HList](n: Nat, l: L)(implicit c: Combinations[n.N, L]): c.Out = c(l)

    type Aux[N <: Nat, L <: HList, Out0 <: HList] = Combinations[N, L] { type Out = Out0 }

    implicit def combination0[L <: HList]: Aux[_0, L, HNil :: HNil] =
      new Combinations[_0, L] {
        type Out = HNil :: HNil
        def apply(l: L): Out = HNil :: HNil
      }

    implicit def combination1[N <: Nat, H, T <: HList, C1 <: HList, C2 <: HList, CM <: HList, CpOut <: HList]
    (implicit
     c1: Aux[N, T, C1],
     c2: Aux[Succ[N], T, C2],
     cm: MapCons.Aux[H, C1, CM],
     cp: Prepend.Aux[CM, C2, CpOut]): Aux[Succ[N], H :: T, CpOut] =
      new Combinations[Succ[N], H :: T] {
        type Out = cp.Out
        def apply(l: H :: T): Out = cp(cm(l.head, c1(l.tail)), c2(l.tail))
      }
  }

  trait LowPriorityCombinations {
    implicit def combinationHNil[N <: Nat] =
      new Combinations[N, HNil] {
        type Out = HNil
        def apply(l: HNil): Out = HNil
      }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy