2014-08-30 2 views
6

Ich versuche, einen Kombinator für die scodec Bibliothek zu schreiben, die eine Codec[K] in eine Codec[L] umwandelt, wo K ein HList und L ist die entfernt Äquivalent HList mit allen Unit Elementen ist.Shapeless: Inversion von filterNot auf einem hList

Die Implementierung der Dekodierung kann durch Dekodieren eines K und anschließendes Ausfiltern aller Unit Elemente erfolgen. Herausfiltern Unit Elemente wird direkt von formlos mit filterNot unterstützt, die diese trivial zu implementieren macht.

Implementierung Codierung wird erreicht, indem ein auf eine LK Umwandlung () an den entsprechenden Indizes Einfügen und dann auf die ursprüngliche Codec[K] delegieren. Ich habe Probleme beim Implementieren der L => K Konvertierung.

def dropUnits[K <: HList, L <: HList](codec: Codec[K])(
    implicit fltr: FilterNot.Aux[K, Unit, L]): Codec[L] = new Codec[L] { 
    override def decode(buffer: BitVector) = 
     codec.decode(buffer).map { case (rest, l) => (rest, l.filterNot[Unit]) } 
    override def encode(xs: L) = { 
     ??? 
    } 
    } 

Ich habe ein paar verschiedene Lösungen ohne Glück ausprobiert. Ist das mit formlos möglich?

Antwort

4

Ich sehe einen Weg, dies nicht ohne eine individuelle Typklasse zu tun, aber dieser Ansatz ist nicht so schlecht:

import shapeless._ 

trait ReUnit[L <: HList, K <: HList] { def apply(l: L): K } 

object ReUnit { 
    implicit object hnilReUnit extends ReUnit[HNil, HNil] { 
    def apply(l: HNil): HNil = HNil 
    } 

    implicit def hlistReUnit[H, L <: HList, K <: HList] 
    (implicit ru: ReUnit[L, K]): ReUnit[H :: L, H :: K] = 
     new ReUnit[H :: L, H :: K] { 
     def apply(l: H :: L): H :: K = l.head :: ru(l.tail) 
     } 

    implicit def unitReUnit[L <: HList, K <: HList] 
    (implicit ru: ReUnit[L, K]): ReUnit[L, Unit :: K] = 
     new ReUnit[L, Unit :: K] { 
     def apply(l: L): Unit :: K =() :: ru(l) 
     } 
} 

def reUnit[L <: HList, K <: HList](l: L)(implicit ru: ReUnit[L, K]) = ru(l) 

Und dann:

scala> type Input = Int :: String :: HNil 
defined type alias Input 

scala> type WithUnits = Int :: Unit :: String :: Unit :: Unit :: HNil 
defined type alias WithUnits 

scala> reUnit[Input, WithUnits](1 :: "foo" :: HNil) 
res0: WithUnits = 1 ::() :: foo ::() ::() :: HNil 

Oder in Ihrem Kontext :

def dropUnits[K <: HList, L <: HList](codec: Codec[K])(implicit 
    fn: FilterNot.Aux[K, Unit, L] 
    ru: ReUnit[L, K] 
): Codec[L] = new Codec[L] { 
    override def decode(buffer: BitVector) = 
    codec.decode(buffer).map { case (rest, l) => (rest, l.filterNot[Unit]) } 
    override def encode(xs: L) = codec.encode(ru(xs)) 
} 

ich habe nicht versucht, diese dropUnits kompilieren, aber es sollte funktionieren.

Der Trick oben ist nur auf die Instanz, die Sie wollen, induktiv arbeiten. Der Basisfall konvertiert einen HNil in einen HNil. Dann, wenn Sie wissen, wie man eine X in eine Y konvertiert, wissen Sie auch, wie man zwei Dinge tut: Konvertieren Sie eine A :: X in eine A :: Y und konvertieren Sie eine X in eine Unit :: Y. Und das ist alles was du brauchst!

+0

Es ist vielleicht erwähnenswert, dass dies Instanzen überfordert - es wird zum Beispiel eine 'ReUnit [Unit :: HNil, Unit :: HNil]' 'finden. Das mag kein Problem für Sie sein, aber wenn Sie es sind, können Sie einfach 'H =!: = Unit' Beweise zu' hlistListReUnit' hinzufügen. –

+0

Danke - Ich habe die Einschränkung 'FilterNot.Aux [K, Unit, L]' in 'ReUnit' übernommen. Nicht sicher, wie ich über das Ergebnis fühle: https://github.com/scodec/scodec/commit/3987a842f97bc0049672d61b4f2c7f526028da4e#diff-d41d8cd98f00b204e9800998ecf8427e – mpilquist

+0

Diese Art der Aufspaltung scheint mir etwas weniger klar, und ich sehe nicht warum Sie müssen 'LH' und' KH' duplizieren, wenn Sie dieselben benötigen. –

Verwandte Themen