Writting algorithm on HList, I need a zipWithIndex function. It is not at the shapeless library by now, so I decided to implement it.
It is quite obvious that it might be implemented as
hlist.zip(indexes)
where indexes is the HList of the indexes (0..n), that probably could be obtained this way:
val indexes = Nat._0 until hlist.length
Issue here is that Nat doesn't have until method. I haven't found any Witness for the HList index to use with HList.map.
What is the method I could use to obtain HList of Nats starting with Nat._0 till hlist.length?
It might make sense to add something like this to Shapeless:
import shapeless._, ops.hlist.Prepend
trait Range[A <: Nat, B <: Nat] extends DepFn0 { type Out <: HList }
object Range {
type Aux[A <: Nat, B <: Nat, Out0 <: HList] = Range[A, B] { type Out = Out0 }
implicit def emptyRange[A <: Nat]: Aux[A, A, HNil] = new Range[A, A] {
type Out = HNil
def apply(): Out = HNil
}
implicit def slightlyBiggerRange[A <: Nat, B <: Nat, OutAB <: HList](implicit
rangeAB: Aux[A, B, OutAB],
appender: Prepend[OutAB, B :: HNil],
witnessB: Witness.Aux[B]
): Aux[A, Succ[B], appender.Out] = new Range[A, Succ[B]] {
type Out = appender.Out
def apply(): Out = appender(rangeAB(), witnessB.value :: HNil)
}
}
def range[A <: Nat, B <: Nat](implicit r: Range[A, B]): r.Out = r()
Now you can write zipWithIndex pretty cleanly:
import ops.hlist.{ Length, Zip }
def zipWithIndex[L <: HList, S <: Nat, R <: HList, Out <: HList](l: L)(implicit
len: Length.Aux[L, S],
range: Range.Aux[nat._0, S, R],
zipper: Zip.Aux[L :: R :: HNil, Out]
): Out = l.zip(range())
And then:
import nat._
type Expected = (Int, _0) :: (Symbol, _1) :: (String, _2) :: HNil
val xs: Expected = zipWithIndex(1 :: 'a :: "foo" :: HNil)
You could also use a fold or a ZippedWithIndex[L <: HList] type class, both of which might be a little more concise, but less clearly composed out of independently useful pieces like Range.
Related
Not sure if the correct term is "distributive property" but I remember learning this in school so here's an example of what I'm trying to do:
Given:
type MyHList = (A :+: B :+: C :+: CNil) :: (Foo :+: Bar :+: CNil) :: HNil
is there any built-in type class in Shapeless that will out this:
type Out = (A, Foo) :+: (A, Bar) :+: (B, Foo) :+: (B, Bar) :+: (C, Foo) :+: (C, Bar) :+: CNil
?
Thanks
I would call such transformation cartesian, tensor or direct product (i.e. a product of each term by each term, on contrary to inner product / scalar product / zipping). Although indeed it relates to distributive law.
I guess there is no such standard type class literally but it can be expressed via standard ones
import shapeless.{:+:, ::, CNil, Coproduct, HList, HNil, Poly1, poly}
import shapeless.ops.coproduct.{FlatMap, Mapper}
trait Cartesian[L <: HList] {
type Out <: Coproduct
}
object Cartesian {
type Aux[L <: HList, Out0 <: Coproduct] = Cartesian[L] { type Out = Out0 }
implicit def mkCartesian[C <: Coproduct, C1 <: Coproduct](implicit
flatMap: FlatMap[C, MapperPoly[C1]]
): Aux[C :: C1 :: HNil, flatMap.Out] = null
trait MapperPoly[C <: Coproduct] extends Poly1
object MapperPoly {
implicit def cse[C <: Coproduct, A](implicit
mapper: Mapper[TuplePoly[A], C]
): poly.Case1.Aux[MapperPoly[C], A, mapper.Out] = null
}
trait TuplePoly[A] extends Poly1
object TuplePoly {
implicit def cse[A, B]: poly.Case1.Aux[TuplePoly[A], B, (A, B)] = null
}
}
implicitly[Cartesian.Aux[MyHList, Out]] // compiles
The type class Cartesian is now acting on type level only. It's possible that on value level its definition would be a little trickier (with poly.Case1.Aux[P, ... for P <: MapperPoly[C], poly.Case1.Aux[P, ... for P <: TuplePoly[A] rather than poly.Case1.Aux[MapperPoly[C], ..., poly.Case1.Aux[TuplePoly[A], ... and using Unpack1, see Filter a HList using a supertype ). Update: Or maybe not :)
Also there is always an option to define a custom type class recursively rather than try to deduce everything to standard type classes.
Here is recursive type-level implementation for multiple HLists of Coproducts (not necessary two)
// transforms an hlist of coproducts into a coproduct of tuples
trait Cartesian[L <: HList] {
type Out <: Coproduct
}
object Cartesian {
type Aux[L <: HList, Out0 <: Coproduct] = Cartesian[L] { type Out = Out0 }
implicit def mkCartesian[L <: HList, C <: Coproduct](implicit
cartesian: CartesianHelper.Aux[L, C],
mapper: coproduct.Mapper[tuplerPoly.type, C]
): Aux[L, mapper.Out] = null
object tuplerPoly extends Poly1 {
implicit def cse[L <: HList](implicit
tupler: hlist.Tupler[L]
): Case.Aux[L, tupler.Out] = null
}
}
// transforms an hlist of coproducts into a coproduct of hlists
trait CartesianHelper[L <: HList] {
type Out <: Coproduct
}
trait LowPriorityHelper1 {
type Aux[L <: HList, Out0 <: Coproduct] = CartesianHelper[L] { type Out = Out0 }
// (a + (a1+...)) * (b1+...) * (c1+...) * ...
// = a * ((b1+...) * (c1+...) * ...)
// + ((a1+...) * (b1+...) * (c1+...) * ...)
implicit def recurse[H, T <: Coproduct, T1 <: HList,
C <: Coproduct, C1 <: Coproduct, C2 <: Coproduct](implicit
ev: T1 <:< (_ :: _),
cartesian: Aux[T1, C],
mapper: coproduct.Mapper.Aux[PrependPoly[H], C, C1],
cartesian1: Aux[T :: T1, C2],
extendBy: coproduct.ExtendBy[C1, C2]
): Aux[(H :+: T) :: T1, extendBy.Out] = null
trait PrependPoly[H] extends Poly1
object PrependPoly {
implicit def cse[H, L <: HList]: poly.Case1.Aux[PrependPoly[H], L, H :: L] = null
}
}
trait LowPriorityHelper extends LowPriorityHelper1 {
implicit def one[C <: Coproduct](implicit
mapper: coproduct.Mapper[prependPoly.type, C]
): Aux[C :: HNil, mapper.Out] = null
object prependPoly extends Poly1 {
implicit def cse[A]: Case.Aux[A, A :: HNil] = null
}
}
object CartesianHelper extends LowPriorityHelper {
implicit def hnil: Aux[HNil, CNil] = null
implicit def cnil[T <: HList]: Aux[CNil :: T, CNil] = null
}
type MyHList1 = (A :+: B :+: C :+: CNil) :: (Foo :+: Bar :+: CNil) :: (X :+: Y :+: CNil) :: HNil
type Out1 = (A, Foo, X) :+: (A, Foo, Y) :+: (A, Bar, X) :+: (A, Bar, Y) :+: (B, Foo, X) :+: (B, Foo, Y) :+:
(B, Bar, X) :+: (B, Bar, Y) :+: (C, Foo, X) :+: (C, Foo, Y) :+: (C, Bar, X) :+: (C, Bar, Y) :+: CNil
implicitly[Cartesian.Aux[MyHList1, Out1]] // compiles
Adding value level:
def cartesian[L <: HList](l: L)(implicit cart: Cartesian[L]): cart.Out = cart(l)
trait Cartesian[L <: HList] extends DepFn1[L] {
type Out <: Coproduct
}
object Cartesian {
type Aux[L <: HList, Out0 <: Coproduct] = Cartesian[L] { type Out = Out0 }
def instance[L <: HList, Out0 <: Coproduct](f: L => Out0): Aux[L, Out0] =
new Cartesian[L] {
override type Out = Out0
override def apply(l: L): Out0 = f(l)
}
implicit def mkCartesian[L <: HList, C <: Coproduct](implicit
cartesian: CartesianHelper.Aux[L, C],
mapper: coproduct.Mapper[tuplerPoly.type, C]
): Aux[L, mapper.Out] = instance(l => mapper(cartesian(l)))
object tuplerPoly extends Poly1 {
implicit def cse[L <: HList](implicit
tupler: hlist.Tupler[L]
): Case.Aux[L, tupler.Out] = at(tupler(_))
}
}
trait CartesianHelper[L <: HList] extends DepFn1[L] {
type Out <: Coproduct
}
trait LowPriorityHelper1 {
type Aux[L <: HList, Out0 <: Coproduct] = CartesianHelper[L] { type Out = Out0 }
def instance[L <: HList, Out0 <: Coproduct](f: L => Out0): Aux[L, Out0] =
new CartesianHelper[L] {
override type Out = Out0
override def apply(l: L): Out0 = f(l)
}
implicit def recurse[H, T <: Coproduct, T1 <: HList,
C <: Coproduct, C1 <: Coproduct, C2 <: Coproduct](implicit
ev: T1 <:< (_ :: _),
cartesian: Aux[T1, C],
prepend: Prepend.Aux[H, C, C1],
cartesian1: Aux[T :: T1, C2],
extendBy: coproduct.ExtendBy[C1, C2]
): Aux[(H :+: T) :: T1, extendBy.Out] =
instance(l => {
val t1 = l.tail
val c = cartesian(t1)
l.head.eliminate(h => {
val c1 = prepend(h, c)
extendBy.right(c1)
}, t => {
val c2 = cartesian1(t :: t1)
extendBy.left(c2)
})
})
// custom type class instead of mapping with a generic Poly
trait Prepend[H, C <: Coproduct] extends DepFn2[H, C] {
type Out <: Coproduct
}
object Prepend {
type Aux[H, C <: Coproduct, Out0 <: Coproduct] = Prepend[H, C] { type Out = Out0 }
def instance[H, C <: Coproduct, Out0 <: Coproduct](f: (H, C) => Out0): Aux[H, C, Out0] =
new Prepend[H, C] {
override type Out = Out0
override def apply(h: H, c: C): Out0 = f(h, c)
}
implicit def cnil[H]: Aux[H, CNil, CNil] = instance((_, _) => unexpected)
implicit def ccons[H, L <: HList, C <: Coproduct](implicit
prepend: Prepend[H, C]
): Aux[H, L :+: C, (H :: L) :+: prepend.Out] =
instance((h, c) =>
c.eliminate(
l => Inl(h :: l),
c => Inr(prepend(h, c))
)
)
}
}
trait LowPriorityHelper extends LowPriorityHelper1 {
implicit def one[C <: Coproduct](implicit
mapper: coproduct.Mapper[prependPoly.type, C]
): Aux[C :: HNil, mapper.Out] = instance(l => mapper(l.head))
object prependPoly extends Poly1 {
implicit def cse[A]: Case.Aux[A, A :: HNil] = at(_ :: HNil)
}
}
object CartesianHelper extends LowPriorityHelper {
implicit def hnil: Aux[HNil, CNil] = instance(_ => unexpected)
implicit def cnil[T <: HList]: Aux[CNil :: T, CNil] = instance(_ => unexpected)
}
val c: C = new C {}
val bar: Bar = new Bar {}
val myHList: MyHList = Inr(Inr(Inl(c))) :: Inr(Inl(bar)) :: HNil
val res = cartesian(myHList)
res: Out // compiles
res == Inr(Inr(Inr(Inr(Inr(Inl((c, bar))))))) // true
I replaced mapping a coproduct with PrependPoly[H] by a custom type class Prepend[H, C <: Coproduct] because generic Poly are tricky and not everything can be done with them on value level.
issue #198: Injecting values to a Poly defined outside of calling method is awkward
issue #154: Improve support for partial application of Polys
Passing an extra argument into a polymorphic function?
Pick out the Nth element of a HList of Lists and return that value as a HList of values
Dynamically parametrize Poly1 function in shapeless
shapeless-dev: How to "parameterize" poly function?
HList folding function that requires the HList
Parameterise filtering of element in of shapeless Hlist of Lists
See also:
Taking HList of Seq[_] and generating Seq[HList] with cartesian product of values
Cartesian product of heterogeneous lists (Haskell)
I am trying to convert my case class into a sequence containing a lens for each field. I've created the following simplified example to highlight the problem that I am having.
The following code will give a runtime error:
import shapeless._
case class Testing(field1: String, field2: Double)
val lenses = Seq(0,1).map(i => lens[Testing] >> i)
whereas the following does not:
import shapeless._
case class Testing(field1: String, field2: Double)
val lens1 = lens[Testing] >> 0
val lens2 = lens[Testing] >> 1
val lenses = Seq(lens1, lens2)
The actual error reads "Expression i does not evaluate to a non-negative Int literal".
I feel like this error message is misleading since the code val lens3 = lens[Testing] >> 2 (i.e. accessing one field too many) would give the same error message.
Has anyone experienced behaviour like this in shapeless? And is there an easier way to extract element lenses for each field in my Case Class into a sequence (i.e. not like #lenses in monocle where you still need to access each lens using the field name)?
lens[Testing] >> 0
lens[Testing] >> 1
are implicitly transformed to
lens[Testing] >> Nat._0
lens[Testing] >> Nat._1
and this works but
val lenses = Seq(0,1).map(i => lens[Testing] >> i)
or val lenses = Seq(Nat._0,Nat._1).map(i => lens[Testing] >> i) doesn't.
Seq(Nat._0,Nat._1) has type Seq[Nat], so i has type Nat (rather than specific Nat._0, Nat._1) and this is too rough.
The following approach with constructing HList of lenses (rather than Seq) seems to work:
import shapeless.{::, Generic, HList, HNil, Lens, MkHListSelectLens}
case class Testing(field1: String, field2: Double)
trait MkLensHlist[A] {
type Out <: HList
def apply(): Out
}
object MkLensHlist {
type Aux[A, Out0 <: HList] = MkLensHlist[A] { type Out = Out0 }
def instance[L, Out0 <: HList](x: Out0): Aux[L, Out0] = new MkLensHlist[L] {
override type Out = Out0
override def apply(): Out0 = x
}
def apply[A](implicit instance: MkLensHlist[A]): instance.Out = instance()
implicit def mk[A, L <: HList, Out <: HList](implicit
gen: Generic.Aux[A, L],
apply: ApplyMkHListSelectLens.Aux[L, Out]
): Aux[A, Out] = instance(apply())
}
trait ApplyMkHListSelectLens[L <: HList] {
type Out <: HList
def apply(): Out
}
object ApplyMkHListSelectLens {
type Aux[L <: HList, Out0 <: HList] = ApplyMkHListSelectLens[L] { type Out = Out0}
def instance[L <: HList, Out0 <: HList](x: Out0): Aux[L, Out0] = new ApplyMkHListSelectLens[L] {
override type Out = Out0
override def apply(): Out0 = x
}
implicit def mk[L <: HList, Out <: HList](implicit
apply: ApplyMkHListSelectLens1.Aux[L, L, Out]
): Aux[L, Out] =
instance(apply())
}
trait ApplyMkHListSelectLens1[L <: HList, L1 <: HList] {
type Out <: HList
def apply(): Out
}
object ApplyMkHListSelectLens1 {
type Aux[L <: HList, L1 <: HList, Out0 <: HList] = ApplyMkHListSelectLens1[L, L1] { type Out = Out0}
def instance[L <: HList, L1 <: HList, Out0 <: HList](x: Out0): Aux[L, L1, Out0] = new ApplyMkHListSelectLens1[L, L1] {
override type Out = Out0
override def apply(): Out0 = x
}
implicit def mk1[L <: HList, H, T <: HList, Out <: HList](implicit
lens: MkHListSelectLens[L, H],
apply: Aux[L, T, Out]
): Aux[L, H :: T, Lens[L, H] :: Out] =
instance(lens() :: apply())
implicit def mk2[L <: HList]: Aux[L, HNil, HNil] =
instance(HNil)
}
MkLensHlist[Testing]
// shapeless.MkHListSelectLens$$anon$36$$anon$17#340f438e :: shapeless.MkHListSelectLens$$anon$36$$anon$17#30c7da1e :: HNil
I would like to extract the key and value of the head of an HList using these two methods:
def getFieldName[K, V](value: FieldType[K, V])(implicit witness: Witness.Aux[K]): K = witness.value
def getFieldValue[K, V](value: FieldType[K, V]): V = value
I tried a few variations of this function, but I couldn't make it work, I think this might be the closest to the right solution:
def getFieldNameValue[Key <: Symbol, Value <: AnyRef, Y <: HList, A <: Product](a : A)
(implicit
gen : LabelledGeneric.Aux[A, FieldType[Key, Value] :: Y],
witness: shapeless.Witness.Aux[Key]) = {
val aGen = gen.to(a).head
(getFieldName(aGen), getFieldValue(aGen))
}
But it throws this exception:
could not find implicit value for parameter gen: shapeless.LabelledGeneric.Aux[Ex,shapeless.labelled.FieldType[Key,Value] :: Y]
I'd like to call it like this:
scala> case class Ex(i: Int, ii: Int)
scala> val ex = Ex(1,2)
scala> getFieldNameValue(ex)
res1: (String, Int) = (i,1)
This variant works:
def getFieldNameValue[A <: Product, Repr <: HList,
K <: Symbol, V, T <: HList](a: A)(implicit
gen: LabelledGeneric.Aux[A, Repr],
ev: Repr <:< (FieldType[K, V] :: T),
witness: Witness.Aux[K]): (String, V) = {
val record: Repr = gen.to(a)
(witness.value.name, record.head)
}
getFieldNameValue(ex) // (i,1)
I guess the trouble was that you tried to do too much work in one step (implicits don't like that).
I have a simple service definition
trait Service[-Req, +Rep] extends (Req => Future[Rep]) {
def apply(request: Req): Future[Rep]
}
and a method how to chain services:
implicit class ServiceOps1[Req, RepIn](service: Service[Req, RepIn]) {
def -->[RepOut](next: Service[RepIn, RepOut]): Service[Req, RepOut] =
(req: Req) => service(req) flatMap next
}
I would like to put all my service (in assumption that they could be composed) into HList and then build from HList a composition of service.
Here is my Resolver
trait Resolver[L <: HList, In] {
type Out
def apply(l: L): Service[In, Out]
}
object Resolver {
def apply[L <: HList, In](implicit resolver: Resolver[L, In]): Aux[L, In, resolver.Out] = resolver
type Aux[L <: HList, In, Out0] = Resolver[L, In] { type Out = Out0 }
implicit def hsingleResolver[I, O, S <: Service[I, O]]: Aux[S :: HNil, I, O] =
new Resolver[S :: HNil, I] {
type Out = O
def apply(l : S :: HNil): Service[I, Out] = l.head
}
implicit def hlistResolver[I, O, S <: Service[I, O], T <: HList](implicit res : Resolver[T, O]): Aux[S :: T, I, res.Out] =
new Resolver[S :: T, I] {
type Out = res.Out
def apply(l: S :: T): Service[I, res.Out] = l.head --> res(l.tail)
}
}
I have a service
object S extends Service[Int, String] {
def apply(request: Int): Future[String] = Future successful request.toString
}
When I try to resolve the simple chain
implicitly[Resolver[S.type :: HNil, Int]].apply(S :: HNil)
I got an implicit not found error.
The problem lies in the type signature of your implicits: implicit def hsingleResolver[I, O, S <: Service[I, O]]: Aux[S :: HNil, I, O]. Here because of S <: Service[I, O] you expect O to be inferred based on the type of S, but unfortunately that's not how it works. The S <: Service[I, O] clause in the type parameter list is not taken into consideration for inferring the type arguments. What happens when you invoke implicitly[Resolver[S.type :: HNil, Int]] is that the compiler sees that S = S.type, I = Int and O is unknown so O = Nothing. Then afterwards it goes on to check that S <: Service[Int,Nothing] which is false and implicit search fails.
So to fix this you have to make the fact that S <: Service[I, O] part of the implicit search/type inference process. For instance in one of these ways:
implicit def hsingleResolver[I, O, S](implicit ev: S <:< Service[I,O]): Aux[S :: HNil, I, O] // option 1
implicit def hsingleResolver[I, O, S]: Aux[(S with Service[I,O]) :: HNil, I, O] // option 2
As a side note: wouldn't it make more sense to define Resolver in the following way?
trait Resolver[L <: HList] {
type In
type Out
def apply(l: L): Service[In, Out]
}
object Resolver {
def apply[L <: HList](implicit resolver: Resolver[L]): Aux[L, resolver.In, resolver.Out] = resolver
type Aux[L <: HList, In0, Out0] = Resolver[L] { type In = In0; type Out = Out0 }
...
}
Because In is also dependent on L, just like Out.
Not sure why this was down voted, maybe you could have made a sample repo available. Anyway, here's a partial answer, maybe this get's you back on track.
1) enable debug options for implicits in your build.sbt: scalacOptions += "-Xlog-implicits"
2) define a resolver for HNil: implicit def hNilResolver[I]: Aux[HNil, I, HNil] = ???
3) following the debug output, fix the remainder :)
I intend to filter on an HList in a covariant manner - I would like to include subclasses as well. So the covariant filter on Foo should capture elements of Foo as well as Bar. I've constructed this example trying out <:!<, to see if it does what I would like it to do.
http://scastie.org/6465
/***
scalaVersion := "2.11.2"
libraryDependencies ++= Seq(
"com.chuusai" %% "shapeless" % "2.0.0"
)
*/
import shapeless._
final class HListOps[L <: HList](l: L) {
trait CoFilter[L <: HList, U] extends DepFn1[L] { type Out <: HList }
object CoFilter {
def apply[L <: HList, U](implicit filter: CoFilter[L, U]): Aux[L, U, filter.Out] = filter
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[L <: HList, H](implicit f: CoFilter[L, H]): Aux[H :: L, H, H :: f.Out] =
new CoFilter[H :: L, H] {
type Out = H :: f.Out
def apply(l: H :: L): Out = l.head :: f(l.tail)
}
implicit def hlistCoFilter2[H, L <: HList, U](implicit f: CoFilter[L, U], e: U <:!< H): Aux[H :: L, U, f.Out] =
new CoFilter[H :: L, U] {
type Out = f.Out
def apply(l: H :: L): Out = f(l.tail)
}
}
def covariantFilter[U](implicit filter: CoFilter[L, U]): filter.Out = filter(l)
}
object Main extends App {
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
implicit def hlistOps[L <: HList](l: L): HListOps[L] = new HListOps(l)
print(l.covariantFilter[Bar] != l)
}
Gives me
[error] /tmp/rendererbI8Iwy0InO/src/main/scala/test.scala:47: could not find implicit value for parameter filter: _1.CoFilter[shapeless.::[Main.Foo,shapeless.::[Main.Bar,shapeless.::[Main.Foo,shapeless.::[Main.Bar,shapeless.HNil]]]],Main.Bar]
[error] print(l.covariantFilter[Bar] != l)
There are a couple of issues here. The first is that your type class is defined inside of your extension class, but you need the instance at the point where you're calling covariantFilter. Maybe the compiler could find it for you, but it doesn't. It's a lot cleaner not to nest the type class anyway, though.
The second issue is that your two hlistCoFilterN cases don't actually capture all the stuff you want. You only tell the compiler what to do in cases where the type of the head is the filter type and where the filter type is not a subtype of the type of the head. What about where the type of the head is a subtype of the filter type? 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 could also remove the H <:!< U constraint and move hlistCoFilter2 to a LowPriorityCoFilter trait. I find this version a little clearer about its intent, but getting rid of the constraint would arguably 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 like this:
scala> l.covariantFilter[Foo] == l
res0: Boolean = true
scala> l.covariantFilter[Bar] == l
res1: Boolean = false
Which I think is what you want.