无形:在HList上反转filterNot

时间:2014-08-30 03:35:52

标签: scala shapeless scodec

我正在尝试为scodec库编写一个组合器,将Codec[K]转换为Codec[L] KHList和{{1} }}是等效的L,删除了所有HList个元素。

实现解码可以通过解码Unit然后过滤掉所有K元素来完成。使用Unit无形状地直接支持过滤掉Unit个元素,这使得实现变得微不足道。

通过将filterNot转换为L,在适当的索引处插入K,然后委派给原始()来实现编码。我虽然在实施Codec[K]转化时遇到了问题。

L => K

我没有运气就尝试过几种不同的解决方案。这可能是没有形状的吗?

1 个答案:

答案 0 :(得分:4)

在没有自定义类型类的情况下,我没有看到这样做的方法,但这种方法并不太糟糕:

import shapeless._

trait ReUnit[L <: HList, K <: HList] { def apply(l: L): K }

object ReUnit {
  implicit object hnilReUnit extends ReUnit[HNil, HNil] {
    def apply(l: HNil): HNil = HNil
  }

  implicit def hlistReUnit[H, L <: HList, K <: HList]
    (implicit ru: ReUnit[L, K]): ReUnit[H :: L, H :: K] =
      new ReUnit[H :: L, H :: K] {
        def apply(l: H :: L): H :: K = l.head :: ru(l.tail)
      }

  implicit def unitReUnit[L <: HList, K <: HList]
    (implicit ru: ReUnit[L, K]): ReUnit[L, Unit :: K] =
       new ReUnit[L, Unit :: K] {
         def apply(l: L): Unit :: K = () :: ru(l)
       }
}

def reUnit[L <: HList, K <: HList](l: L)(implicit ru: ReUnit[L, K]) = ru(l)

然后:

scala> type Input = Int :: String :: HNil
defined type alias Input

scala> type WithUnits = Int :: Unit :: String :: Unit :: Unit :: HNil
defined type alias WithUnits

scala> reUnit[Input, WithUnits](1 :: "foo" :: HNil)
res0: WithUnits = 1 :: () :: foo :: () :: () :: HNil

或者在您的背景下:

def dropUnits[K <: HList, L <: HList](codec: Codec[K])(implicit
  fn: FilterNot.Aux[K, Unit, L]
  ru: ReUnit[L, K]
): Codec[L] = new Codec[L] {
  override def decode(buffer: BitVector) = 
    codec.decode(buffer).map { case (rest, l) => (rest, l.filterNot[Unit]) }
  override def encode(xs: L) = codec.encode(ru(xs))
}

我没有尝试编译此dropUnits,但它应该有用。

上面的诀窍就是努力实现你想要的实例。基本情况是将HNil转换为HNil。然后,如果您知道如何将X转换为Y,您还知道如何做两件事:将A :: X转换为A :: Y并转换{{1到X。这就是你所需要的一切!