在 HList 上做协变过滤器 [英] Do a covariant filter on an HList
问题描述
我打算以协变方式过滤 HList - 我也想包括子类.所以 Foo
上的协变过滤器应该捕获 Foo
和 Bar
的元素.我已经构建了这个示例,尝试使用 <:!<
,看看它是否能满足我的要求.
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.
/***
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)
}
给我
[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)
推荐答案
这里有几个问题.第一个是你的类型类是在你的扩展类中定义的,但是你需要在调用 covariantFilter
的地方的实例.也许编译器可以为你找到它,但它没有.不过,无论如何不要嵌套类型类要干净得多.
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.
第二个问题是您的两个 hlistCoFilterN
案例实际上并没有捕获您想要的所有内容.你只告诉编译器在头部类型是过滤器类型并且过滤器类型不是头部类型的子类型的情况下做什么.如果头部的类型是过滤器类型的子类型呢?你可能想要这样的东西:
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)
}
(作为记录,您还可以删除 H <:!< U
约束并将 hlistCoFilter2
移动到 LowPriorityCoFilter
trait.我发现这个版本的意图更清晰一些,但摆脱约束可能会更清晰.)
(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
我认为这是你想要的.
这篇关于在 HList 上做协变过滤器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!