Concat_via_type_classes trait HList case class HCons[H, T <: HList](head: H, tail: T) extends HList case object HNil extends HList type HNil = HNil.type // type class trait Concat[L1 <: HList, L2 <: HList] { type Out <: HList def apply(l1: L1, l2: L2): Out } object Concat { type Aux[L1 <: HList, L2 <: HList, Out0 <: HList] = Concat[L1, L2] { type Out = Out0 } def apply[L1 <: HList, L2 <: HList, Out0 <: HList](f: (L1, L2) => Out0): Aux[L1, L2, Out0] = new Concat[L1, L2] { override type Out = Out0 override def apply(l1: L1, l2: L2): Out = f(l1, l2) } implicit def hNilConcat[L <: HList]: Aux[HNil, L, L] = Concat((_, l) => l) implicit def hConsConcat[H, T <: HList, L <: HList, T_concatL <: HList](implicit concat: Aux[T, L, T_concatL]): Aux[HCons[H, T], L, HCons[H, T_concatL]] = Concat { case (HCons(h, t), l) => HCons(h, concat(t, l)) } object op { implicit class ConcatOp[L1 <: HList](l1: L1) { def concat[L2 <: HList](l2: L2)(implicit concat: Concat[L1, L2]): concat.Out = concat(l1, l2) } } } import Concat.op._ HCons(1, HCons("a", HCons(true, HNil))).concat(HCons(1L, HCons(1.0, HNil))) Concat_via_type_projections trait HList { type Concat[L <: HList] <: HList def concat[L <: HList](l: L): Concat[L] } case class HCons[H, T <: HList](head: H, tail: T) extends HList { override type Concat[L <: HList] = HCons[H, T#Concat[L]] override def concat[L <: HList](l: L): Concat[L] = HCons(head, tail.concat(l)) } case object HNil extends HList { override type Concat[L <: HList] = L override def concat[L <: HList](l: L): Concat[L] = l } type HNil = HNil.type HCons(1, HCons("a", HCons(true, HNil))).concat(HCons(1L, HCons(1.0, HNil))) HList trait HList case class HCons[H, T <: HList](head: H, tail: T) extends HList case object HNil extends HList type HNil = HNil.type HCons(1, HCons("a", HCons(true, HNil))) : HCons[Int, HCons[String, HCons[Boolean, HNil]]]