无形:在HList上反转filterNot

mpi*_*ist 6 scala shapeless scodec

我正在尝试为scodec库编写一个组合器,它将Codec[K]in转换为Codec[L]where K是a HList并且与删除的所有元素L等效.HListUnit

实现解码可以通过解码a K然后过滤掉所有Unit元素来完成.过滤掉Unit元素直接由无形使用支持filterNot,这使得实现这一点变得微不足道.

实现编码是通过将La 转换为a K,()在适当的索引处插入,然后委托给原始代码来完成的Codec[K].我在实现L => K转换时遇到了麻烦.

def dropUnits[K <: HList, L <: HList](codec: Codec[K])(
  implicit fltr: FilterNot.Aux[K, Unit, L]): 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) = {
      ???
    }
  }
Run Code Online (Sandbox Code Playgroud)

没有运气,我尝试了几种不同的解决方案.这可能是无形的吗?

Tra*_*own 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)
Run Code Online (Sandbox Code Playgroud)

进而:

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
Run Code Online (Sandbox Code Playgroud)

或者在您的上下文中:

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))
}
Run Code Online (Sandbox Code Playgroud)

我没有尝试编译它,dropUnits但它应该可以工作。

上面的技巧只是为了归纳出你想要的实例。基本情况是将 an 转换HNil为 an HNil。那么,如果您知道如何将 an 转换X为 a Y,那么您还知道如何执行两件事:将 an 转换A :: X为 anA :: Y以及将 an 转换X为 a Unit :: Y。这就是您所需要的!