You can do this with a class of a custom type, which indicates that there is only one TB , and all other TA elements. If you plan to create this list inductively, you will see that you need to handle two cases - either all that you have seen so far is TA (which we can observe with ToList[T, TA] ), and the current element is TB , or you have already seen one TB , and the current element is TA :
import shapeless._, ops.hlist.{ ToList } trait T case class TA() extends T case class TB() extends T trait UniqueTB[L <: HList] extends DepFn1[L] { type Out = TB def apply(l: L): TB } object UniqueTB { def apply[L <: HList](implicit utb: UniqueTB[L]): UniqueTB[L] = utb def getTB[L <: HList](l: L)(implicit utb: UniqueTB[L]): TB = utb(l) implicit def firstTB[T <: HList]( implicit tl: ToList[T, TA] ): UniqueTB[TB :: T] = new UniqueTB[TB :: T] { def apply(l: TB :: T): TB = l.head } implicit def afterTB[T <: HList]( implicit utb: UniqueTB[T] ): UniqueTB[TA :: T] = new UniqueTB[TA :: T] { def apply(l: TA :: T): TB = utb(l.tail) } }
And then:
scala> UniqueTB[TB :: HNil] res0: UniqueTB[shapeless.::[TB,shapeless.HNil]] = UniqueTB$$anon$1@385c6929 scala> UniqueTB[TA :: TB :: HNil] res1: UniqueTB[shapeless.::[TA,shapeless.::[TB,shapeless.HNil]]] = UniqueTB$$anon$2@682dd97e scala> UniqueTB[TA :: TB :: TA :: HNil] res2: UniqueTB[shapeless.::[TA,shapeless.::[TB,shapeless.::[TA,shapeless.HNil]]]] = UniqueTB$$anon$2@5ef48f82 scala> UniqueTB[TB :: HNil] res3: UniqueTB[shapeless.::[TB,shapeless.HNil]] = UniqueTB$$anon$1@33be241 scala> UniqueTB[TA :: HNil] <console>:25: error: could not find implicit value for parameter utb: UniqueTB[shapeless.::[TA,shapeless.HNil]] UniqueTB[TA :: HNil] ^ scala> UniqueTB[HNil] <console>:25: error: could not find implicit value for parameter utb: UniqueTB[shapeless.HNil] UniqueTB[HNil] ^ scala> UniqueTB[TB :: TB :: HNil] <console>:25: error: could not find implicit value for parameter utb: UniqueTB[shapeless.::[TB,shapeless.::[TB,shapeless.HNil]]] UniqueTB[TB :: TB :: HNil] ^
I gave the type class an operation that returns TB , but if you do not need it, you can leave it without methods.
source share