5

作为 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]

但是,这不起作用说

无法证明 shapeless.::[Boolean,shapeless.::[shapeless.::[Int,shapeless.::[Int,shapeless.::[Int,shapeless.HNil]]],shapeless.::[Boolean ,shapeless.HNil]]] 可以与 cmd504.MyPacket 相互转换。

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

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

一切都按预期编译和工作。我的直觉告诉我必须将编解码器“展平”(基于错误消息中的两个 HNil),但我无法展平编解码器本身或内部 HList 表示。

4

1 回答 1

4

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

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

我们想创建一个x包含 before 和 after的新列表xs。我们可以使用+:and :+

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 的情况下,没有+:运算符,但有::and :+,您可以像在值级别使用列表版本一样使用它们:

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,然后将其展平,但:+更为惯用。

于 2016-01-11T12:16:34.173 回答