压扁任意嵌套的编解码器?

时间:2016-01-11 01:07:02

标签: scala shapeless scodec

作为SCodec的新用户,有一个相当的学习曲线。尽管阅读了源代码和文档,我还是遇到了一些似乎无法解决的问题。

我希望能够将流行的编解码器定义为像这样的函数

def packedByte : Codec[Int :: Int :: Int :: HNil] = uint(4) :: uint(2) :: uint(2)

然后将它们组合到更高级别的编解码器中,这些编解码器对像

这样的案例类进行解码和编码
case class MyPacket(foo : Boolean, first : Int, second : Int, third : Int, bar : Boolean)
def packet : Codec[MyPacket] = (bool :: packedByte :: bool).as[MyPacket]

但是,这不起作用

  

无法证明自己是无形的。:[布尔,无形。:无形。:我的,无形。:我是无形的。:无瑕疵。形状,无形。: :[Boolean,shapeless.HNil]]]可以转换为cmd504.MyPacket。

然而,当我“内联”packedByte时,就像

一样
def packetInline : Codec[MyPacket] = (bool :: uint(4) :: uint(2) :: uint(2) :: bool).as[MyPacket]

所有内容都按预期编译和工作。我的直觉告诉我,编解码器必须“扁平化”(基于错误消息中的两个HNils),但我无法压缩编解码器本身或内部HList表示。

1 个答案:

答案 0 :(得分:4)

通过考虑在类似情况下如何使用普通值级别列表来开始推理hlists通常很有用。例如,假设我们有一个值和一个列表:

val x = 0
val xs = List(1, 2, 3)

我们希望在x之前和之后创建一个xs的新列表。我们可以使用+::+

scala> x +: xs :+ x
res0: List[Int] = List(0, 1, 2, 3, 0)

或者:

scala> x :: (xs :+ x)
res1: List[Int] = List(0, 1, 2, 3, 0)

对于Scodec,没有+:运算符,但有:::+,您可以完全按照在值级别使用列表版本的方式使用它们:

import scodec._, scodec.codecs._, shapeless._

def packedByte: Codec[Int :: Int :: Int :: HNil] =
  uint(4) :: uint(2) :: uint(2)

case class MyPacket(
  foo: Boolean,
  first: Int,
  second: Int,
  third: Int,
  bar: Boolean
)

def packet: Codec[MyPacket] = (bool :: (packedByte :+ bool)).as[MyPacket]

有可能构建一个嵌套的hlist,然后将其展平,但:+更加惯用。