Свести произвольно вложенный кодек?

Как новый пользователь 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.::[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]

Все компилируется и работает как положено. Моя интуиция подсказывает мне, что кодек должен быть "сплющен" (основываясь на двух HNils в сообщении об ошибке), но я не смог сгладить сам кодек или внутреннее представление HList.

1 ответ

Решение

Часто полезно начинать рассуждать о списках, думая о том, как вы будете работать с обычными списками уровня значений в аналогичной ситуации. Например, предположим, что у нас есть значение и список:

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]

Можно было бы создать вложенный список и затем сгладить его, но :+ гораздо идиоматичнее.

Другие вопросы по тегам