Smooth an arbitrary nested codec?

As a new SCodec user, there is a pretty learning curve. I fell into a trap that I cannot solve, despite reading the source and documents.

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 into higher-level codecs like this, which decode and encode from case classes like this

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

But that doesn't work, saying

Failed to prove that shapeless. :: [Boolean, shapeless. :: :: shapeless. :: [Int, shapeless. :: [Int, shapeless. :: [Int, shapeless.HNil]]], shapeless. :: [ Boolean, shapeless.HNil]]] can be converted to / from cmd504.MyPacket.

However, when I " packedByte " packedByte , for example

 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 Codec should be flattened (based on two HNils in the error message), but I was not able to smooth out the codec itself or the internal representation of the HList.

+5
source share
1 answer

It is often useful to start talking about hlists when thinking about how you will work with regular value-level lists in a similar situation. For example, suppose we have 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 . 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 is no +: operator, but there is :: and :+ , and you can use them in the same way as you would use list versions at the level of values:

 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] 

One could build a nested hlist and then flatten it, but :+ much more idiomatic.

+4
source

Source: https://habr.com/ru/post/1240302/


All Articles