2016-01-11 10 views
5

Come nuovo utente di SCodec, c'è una vera e propria curva di apprendimento. Ho avuto un problema che non riesco a risolvere nonostante abbia letto la fonte e i documenti.Appiattisci un codec arbitrariamente annidato?

Voglio essere in grado di definire i codec popolari come funzioni come questo

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

E poi combinarle in per codec di livello superiore come questo, che decodifica per e codificare dalle classi caso come questo

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

Ma, questo non funziona dicendo

Potrebbe non dimostrare che shapeless.::[Boolean,shapeless.::[shap eless.::[Int,shapeless.::[Int,shapeless.::[Int,shapeless.HNil]]] ,shapeless.::[Boolean ,shapeless.HNil]]] può essere convertito in/da cmd504.MyPacket .

Eppure, quando ho "in linea", il packedByte, come

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

Tutto compila e funziona come previsto. La mia intuizione mi dice che il Codec deve essere "appiattito" (basato sui due HNils nel messaggio di errore), ma non sono stato in grado di appiattire il Codec stesso o la rappresentazione HList interna.

+0

come mai funziona senza ':: HNil' alla fine? –

+1

@ Łukasz Vedi [questo metodo] (https://github.com/scodec/scodec/blob/v1.8.3/shared/src/main/scala/scodec/package.scala#L269) - è uno speciale operatore Scodec, non di Shapeless '::'. –

+0

Va bene, grazie! Non lo sapevo. –

risposta

4

Spesso è utile iniziare a ragionare sulle hlists pensando a come si lavora con gli ordinari elenchi a livello di valore in una situazione simile. Ad esempio, supponiamo che abbiamo un valore e un elenco:

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

E vogliamo creare un nuovo elenco con x sia prima che dopo xs. Possiamo usare +: e :+:

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

Oppure:

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

Nel caso di Scodec, non c'è +: operatore, ma ci sono :: e :+, e si possono usare esattamente come si farebbe utilizzare le versioni elenco al livello di valore:

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] 

Sarebbe possibile costruire un neste d elencare e quindi appiattire, ma :+ è molto più idiomatico.

+0

Ah, ho appena scoperto l'operatore: + la scorsa notte, ma stavo avendo problemi a utilizzarlo a causa dei reclami del compilatore sull'associatività. Sembra che quei paren fossero la chiave. Grazie per l'aiuto! – RAX