Say we have the following classes and some values (in Scala):
class A[T](val x: T)
class B[T](x: T, val y: T) extends A[T](x)
val x1 = new A("test")
val x2 = new B(1,2)
val x3 = new B("foo","bar")
val x4 = new A(1)
Further, we define the following polymorphic function value (using shapeless):
object f extends (A ~> Option) {
def apply[T](s: A[T]) = Some(s.x)
}
Now we can call:
f(x1); f(x2); f(x3); f(x4)
Which all succeed (and should IMHO). However:
val list = x1 :: x2 :: x3 :: x4 :: HNil
list.map(f)
// could not find implicit value for parameter mapper:
// shapeless.Mapper[f.type,shapeless.::[A[String],shapeless.::[
// B[Int],shapeless.::[B[String],shapeless.::[A[Int],shapeless.HNil]]]]]
Where I was expecting:
Some("test") :: Some(1) :: Some("foo") :: Some(1) :: HNil
Note that this works:
val list2 = x1 :: x4 :: HNil // only instances of A
list2.map(f)
UPDATE
It seems that if we specify each case separately, it's fine:
object f extends Poly1 {
implicit def caseA[T] = at[A[T]]{s => Some(s.x)}
implicit def caseB[T] = at[B[T]]{s => Some(s.x)}
}
However, trying to express this a bit smarter, does not work (not even for simple applications):
object f extends Poly1 {
implicit def caseA[T, S <: A[T]] = at[S]{s => Some(s.x)}
}
Your best options are one of @TravisBrown's suggestion to use a view bound,
or, more or less equivalently, a type constraint,
or a variation on your two case solution which factors out the commonality,
It's unlikely that shapeless's polymorphic function values will be changed to directly support the kind of argument type variance needed to make the initial definition work as you wanted, because that would conflict with the (highly desirable IMO) ability to discriminate type-specific cases very precisely.