There are a couple of issues here. First, your class type is defined inside your extension class, but you need an instance at the point where you call covariantFilter . Perhaps the compiler may find it for you, but it is not. It is much cleaner to not insert a type class anyway.
The second problem is that your two cases of hlistCoFilterN do not actually capture everything you need. You only tell the compiler what to do when the head type is a filter type and where the filter type is not a subtype of the head type. How about where the head type is a filter type subtype? You probably want something like this:
import shapeless._ trait CoFilter[L <: HList, U] extends DepFn1[L] { type Out <: HList } object CoFilter { def apply[L <: HList, U](implicit f: CoFilter[L, U]): Aux[L, U, f.Out] = f type Aux[L <: HList, U, Out0 <: HList] = CoFilter[L, U] { type Out = Out0 } implicit def hlistCoFilterHNil[L <: HList, U]: Aux[HNil, U, HNil] = new CoFilter[HNil, U] { type Out = HNil def apply(l: HNil): Out = HNil } implicit def hlistCoFilter1[U, H <: U, T <: HList] (implicit f: CoFilter[T, U]): Aux[H :: T, U, H :: f.Out] = new CoFilter[H :: T, U] { type Out = H :: f.Out def apply(l: H :: T): Out = l.head :: f(l.tail) } implicit def hlistCoFilter2[U, H, T <: HList] (implicit f: CoFilter[T, U], e: H <:!< U): Aux[H :: T, U, f.Out] = new CoFilter[H :: T, U] { type Out = f.Out def apply(l: H :: T): Out = f(l.tail) } } implicit final class HListOps[L <: HList](val l: L) { def covariantFilter[U](implicit filter: CoFilter[L, U]): filter.Out = filter(l) }
(For the record, you can also remove the restriction H <:!< U hlistCoFilter2 H <:!< U and move hlistCoFilter2 to the hlistCoFilter2 trait. I find this version a little clearer regarding its intent, but getting rid of the restriction may be cleaner.)
Now if you have the following:
class Foo(val foo: Int) class Bar(val bar: Int) extends Foo(bar) val l = new Foo(1) :: new Bar(2) :: new Foo(3) :: new Bar(4) :: HNil
Your filter will work as follows:
scala> l.covariantFilter[Foo] == l res0: Boolean = true scala> l.covariantFilter[Bar] == l res1: Boolean = false
I think this is what you want.