在HList上执行协变过滤器

时间:2014-09-07 18:55:40

标签: scala shapeless

我打算以协变方式过滤HList - 我也希望包含子类。因此,Foo上的协变过滤器应捕获FooBar的元素。我构建了这个示例,尝试<:!<,看看它是否符合我的意愿。

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)

}

给我

[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)

1 个答案:

答案 0 :(得分:6)

这里有几个问题。第一个是你的类型类是在你的扩展类中定义的,但你需要在你调用covariantFilter的位置实例。也许编译器可能为你找到它,但事实并非如此。不过,最好不要嵌套类型类。

第二个问题是你的两个hlistCoFilterN个案实际上没有抓住你想要的所有东西。您只告诉编译器在头部类型是过滤器类型以及过滤器类型不是头部类型的子类型的情况下该怎么做。头部的类型在何处是过滤器类型的子类型?你可能想要这样的东西:

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特征。我发现这个版本对其意图更清楚一点,但摆脱了约束可以说是更清洁。)

现在,如果你有以下内容:

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

您的过滤器将按以下方式运行:

scala> l.covariantFilter[Foo] == l
res0: Boolean = true

scala> l.covariantFilter[Bar] == l
res1: Boolean = false

我认为你想要的是什么。