I'm trying to add a constraint on an HList (from Shapeless):
TA (from 0 to N);TB.My example has this type hierarchy:
trait T
case class TA extends T
case class TB extends T
To give examples:
tb :: HNil is validta :: tb ::HNil is validta :: tb :: ta :: HNil is validta :: HNil is invalidHNil is invalidI cannot figure out how to express this as a constraint.
You can do this with a custom type class that witnesses that there's exactly one TB and all the other elements are TA. If you imagine building up this list inductively, you'll see there are two cases you need to handle—either everything you've seen so far is a TA (which we can witness with a ToList[T, TA]) and the current element is a TB, or you've already seen a single TB and the current element is a 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've given the type class an operation that returns the TB, but if you don't need that you could leave it method-less.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With