2016-01-11 32 views
5

Jako nowy użytkownik SCodec, istnieje dość krzywej uczenia się. Uderzyłem w szkopuł, którego nie mogę rozwiązać pomimo czytania źródła i dokumentów.Spłaszczyć kodek zagnieżdżony dowolnie?

Chcę być w stanie określić popularnych kodeków, funkcje jak ten

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

a następnie połączyć je w wyższych kodeków poziomie jak ten, który dekoduje do kodowania z klas takim wypadku

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

Ale to nie zadziała mówiąc

nie można udowodnić, że shapeless.::[Boolean,shapeless.::[shap eless.::[Int,shapeless.::Nie jest niewypowiedziany.::Niektowo nie jest bezużyteczny.HNyl]]], bezkształtny.::[Boolean,shapeless.HNil]]] można przekonwertować do/z cmd504.MyPacket .

Jednak kiedy "inline" THE packedByte, jak

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

Wszystko kompiluje i działa zgodnie z oczekiwaniami. Moja intuicja podpowiada mi, że kodek musi być "spłaszczony" (oparty na dwóch HNils w komunikacie o błędzie), ale nie byłem w stanie spłaszczyć samego Codec ani wewnętrznej reprezentacji HList.

+0

dlaczego to działa bez ':: HNil' na końcu? –

+1

@ Łukasz Zobacz [ta metoda] (https://github.com/scodec/scodec/blob/v1.8.3/shared/src/main/scala/scodec/package.scala#L269) - to specjalny operator Scodec, nie Shapeless's '::'. –

+0

W porządku, dziękuję! Tego nie wiedziałem. –

Odpowiedz

4

Często warto zacząć myśleć o hlistach, myśląc o tym, jak pracować z zwykłymi listami wartości w podobnej sytuacji. Na przykład, załóżmy, że mamy wartość oraz listę:

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

I chcemy, aby utworzyć nową listę z x zarówno przed jak i po xs. Możemy użyć +: i :+:

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

czyli

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

W przypadku Scodec, nie ma operator +:, ale istnieją :: i :+ i można wykorzystać je dokładnie tak samo, jak użyj wersji listy na poziomie wartości:

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] 

Możliwe byłoby skonstruowanie gniazda d hlist, a następnie spłaszcz ją, ale :+ jest znacznie bardziej idiomatyczny.

+0

Ach, właśnie odkryłem operatora: + zeszłej nocy, ale miałem problemy z jego używaniem ze względu na skargi dotyczące skojarzeń. Wygląda na to, że te pareny były kluczem. Dzięki za pomoc! – RAX