简体   繁体   English

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

[英]Flatten an arbitrarily nested codec?

As a new user of SCodec, there is quite a learning curve. 作为SCodec的新用户,有一个相当的学习曲线。 I've hit a snag that I can't seem to solve despite reading the source and docs. 尽管阅读了源代码和文档,我还是遇到了一些似乎无法解决的问题。

I want to be able to define popular codecs as functions like this 我希望能够将流行的编解码器定义为这样的函数

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

And then combine them in to higher level codecs like this which decode to and encode from case classes like this 然后将它们组合到更高级别的编解码器中,这样可以对像这样的case类进行解码和编码

case class MyPacket(foo : Boolean, first : Int, second : Int, third : Int, bar : Boolean)
def packet : Codec[MyPacket] = (bool :: packedByte :: bool).as[MyPacket]

But, this doesn't work saying 但是,这不起作用

Could not prove that shapeless.::[Boolean,shapeless.::[shapeless.::[Int,shapeless.::[Int,shapeless.::[Int,shapeless.HNil]]],shapeless.::[Boolean,shapeless.HNil]]] can be converted to/from cmd504.MyPacket. 无法证明自己没有形状。:[布尔,无形。:无形。:[Int,Shapeless。::] Int,Shapeless。:::Int ,shapeless .HNil]]],形状::[布尔,shapeless.HNil]]]可以转换为/从cmd504.MyPacket。

Yet, when I "inline" the packedByte , like 然而,当我“内联” packedByte ,就像

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

Everything compiles and works as expected. 一切都按预期编译和工作。 My intuition tells me that the Codec must be "flattened" (based off of the two HNils in the error message), but I have been unable to flatten the Codec itself or the internal HList representation. 我的直觉告诉我,编解码器必须“扁平化”(基于错误消息中的两个HNils),但我无法压缩编解码器本身或内部HList表示。

It's often useful to start reasoning about hlists by thinking about how you'd work with ordinary value-level lists in a similar situation. 通过考虑在类似情况下如何使用普通的价值级列表来开始推理hlists通常很有用。 For example, suppose we've got a value and a list: 例如,假设我们有一个值和一个列表:

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

And we want to create a new list with x both before and after xs . 我们想在xs之前和之后创建一个包含x的新列表。 We can use +: and :+ : 我们可以使用+::+

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

Or: 要么:

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

In the case of Scodec, there's no +: operator, but there are :: and :+ , and you can use them exactly as you would use the list versions at the value level: 在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]

It would be possible to construct a nested hlist and then flatten it, but :+ is far more idiomatic. 有可能构建一个嵌套的hlist然后展平它,但是:+更加惯用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM