在 HList 上做协变过滤器 [英] Do a covariant filter on an HList

查看:43
本文介绍了在 HList 上做协变过滤器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我打算以协变方式过滤 HList - 我也想包括子类.所以 Foo 上的协变过滤器应该捕获 FooBar 的元素.我已经构建了这个示例,尝试使用 <:!<,看看它是否能满足我的要求.

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)

}

给我

[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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆