6

I'm trying to write a combinator for the scodec library that converts a Codec[K] in to a Codec[L] where K is an HList and L is the equivalent HList with all Unit elements removed.

Implementing decoding can be done by decoding a K and then filtering out all Unit elements. Filtering out Unit elements is directly supported by shapeless using filterNot, which makes this trivial to implement.

Implementing encoding is accomplished by converting an L to a K, inserting () at the appropriate indices, and then delegating to the original Codec[K]. I'm having trouble implementing the L => K conversion though.

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) = {
      ???
    }
  }

I've tried a few different solutions without luck. Is this possible with shapeless?

4

1 回答 1

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,但它应该可以工作。

上面的技巧只是针对你想要的实例进行归纳。基本情况是将 an 转换HNilHNil. 然后,如果您知道如何将 an 转换X为 a Y,那么您也知道如何做两件事:将 an 转换A :: X为 anA :: Y和将 an 转换X为 a Unit :: Y。这就是你所需要的!

于 2014-08-30T04:13:04.817 回答