用于无形Hlist的通用Poly2文件夹案例

时间:2015-02-18 18:25:24

标签: scala generics fold shapeless hlist

我正在尝试转换以下HList

Some(C(15)) :: None :: Some(B(55)) :: None :: Some(A(195)) :: HNil

C(15) :: B(55) :: A(195) :: HNil

这就是我现在所拥有的:

  import shapeless._
  case class A(value: Int)
  case class B(value: Int)
  case class C(value: Int)

  trait folderLP extends Poly2 {
    implicit def default[T, L <: HList] = at[T, L]((acc, t) => acc)
  }
  object folder extends folderLP {
    implicit def none[T, L <: HList] = at[None.type, L]((t, acc) => acc)

    implicit def someDiameter[T, L <: HList] = at[Some[C], L]((t, acc) => t.get :: acc)

    implicit def someRatio[T, L <: HList] = at[Some[B], L]((t, acc) => t.get :: acc)

    implicit def someWidth[T, L <: HList] = at[Some[A], L]((t, acc) => t.get :: acc)
  }
  val test = Some(C(15)) :: None :: Some(B(55)) :: None :: Some(A(195)) :: HNil

  val filtered = test.foldRight[HList](HNil)(folder)

这有效,但我想使这个通用,以便它适用于包含在Some中的任何类型,而无需编写每个案例

1 个答案:

答案 0 :(得分:7)

首先是字面答案。请注意,大多数T类型参数未被使用。您可以使用T使您的函数与Some[T]类型的任何元素匹配:

trait folderLP extends Poly2 {
  implicit def default[T, L <: HList] = at[T, L]((_, acc) => acc)
}

object folder extends folderLP {
  implicit def some[T, L <: HList] = at[Some[T], L]((t, acc) => t.get :: acc)
}

请注意,如果您在none中切换参数顺序,则甚至不需要default个案。

另请注意,您可能希望使用filtered的以下定义:

val filtered = test.foldRight(HNil: HNil)(folder)

这一个将HNil静态输入为HNil而不是HList,这对于你想要做的任何事情都很有用 - 例如试试原始版本上的filtered.length,然后是此版本。

你甚至不需要折叠这个操作,但flatMap会这样做:

trait filterLP extends Poly1 {
  implicit def any[T] = at[T](_ => HNil)
}

object filter extends filterLP {
  implicit def some[T] = at[Some[T]](_.get :: HNil)
}

然后:

val filtered = test.flatMap(filter)

最后,值得注意的是,这仅适用于HList NoneSome元素被静态输入为NoneSome的情况 - 例如,Some[A]静态输入的Option[A]将被过滤掉。这使得它有点无用(至少我看不到实际用途),但是如果你在编译时不知道是否{{1},那么就没有任何方法可以执行这种类型级别的过滤器是空的。