I see no way to do this without a special type, but this approach is not so bad:
import shapeless._ trait ReUnit[L <: HList, K <: HList] { def apply(l: L): K } object ReUnit { implicit object hnilReUnit extends ReUnit[HNil, HNil] { def apply(l: HNil): HNil = HNil } implicit def hlistReUnit[H, L <: HList, K <: HList] (implicit ru: ReUnit[L, K]): ReUnit[H :: L, H :: K] = new ReUnit[H :: L, H :: K] { def apply(l: H :: L): H :: K = l.head :: ru(l.tail) } implicit def unitReUnit[L <: HList, K <: HList] (implicit ru: ReUnit[L, K]): ReUnit[L, Unit :: K] = new ReUnit[L, Unit :: K] { def apply(l: L): Unit :: K = () :: ru(l) } } def reUnit[L <: HList, K <: HList](l: L)(implicit ru: ReUnit[L, K]) = ru(l)
And then:
scala> type Input = Int :: String :: HNil defined type alias Input scala> type WithUnits = Int :: Unit :: String :: Unit :: Unit :: HNil defined type alias WithUnits scala> reUnit[Input, WithUnits](1 :: "foo" :: HNil) res0: WithUnits = 1 :: () :: foo :: () :: () :: HNil
Or in your context:
def dropUnits[K <: HList, L <: HList](codec: Codec[K])(implicit fn: FilterNot.Aux[K, Unit, L] ru: ReUnit[L, K] ): Codec[L] = new Codec[L] { override def decode(buffer: BitVector) = codec.decode(buffer).map { case (rest, l) => (rest, l.filterNot[Unit]) } override def encode(xs: L) = codec.encode(ru(xs)) }
I did not try to compile this dropUnits , but it should work.
The trick above is simply to work on what you want inductively. The base register converts HNil to HNil . Then, if you know how to convert X to Y , you also know how to do two things: convert A :: X to A :: Y and convert X to Unit :: Y And thatβs all you need!