2015-03-24 11 views
20

Ich denke an etwas ähnliches wie Safely copying fields between case classes of different types zu tun, sondern mit neu geordnet Felder, dhShapeless - wiederum eine Fallklasse in eine andere mit Feldern in einer anderen Reihenfolge

case class A(foo: Int, bar: Int) 
case class B(bar: Int, foo: Int) 

Und ich möchte etwas haben, eine drehen A(3, 4) in eine B(4, 3) - formlos LabelledGeneric in den Sinn kommt, aber

LabelledGeneric[B].from(LabelledGeneric[A].to(A(12, 13))) 

Ergebnisse in

<console>:15: error: type mismatch; 
found : shapeless.::[shapeless.record.FieldType[[email protected]@[Symbol,String("foo")],Int],shapeless.::[shapeless.record.FieldType[[email protected]@[Symbol,String("bar")],Int],shapeless.HNil]] 
    (which expands to) shapeless.::[Int with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("foo")],Int],shapeless.::[Int with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("bar")],Int],shapeless.HNil]] 
required: shapeless.::[shapeless.record.FieldType[[email protected]@[Symbol,String("bar")],Int],shapeless.::[shapeless.record.FieldType[[email protected]@[Symbol,String("foo")],Int],shapeless.HNil]] 
    (which expands to) shapeless.::[Int with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("bar")],Int],shapeless.::[Int with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("foo")],Int],shapeless.HNil]] 
       LabelledGeneric[B].from(LabelledGeneric[A].to(A(12, 13))) 
                 ^

Wie ordne ich die Felder im Datensatz (?) Neu, so dass dies mit einem Minimum an Standardwerten funktionieren kann?

+4

Ich bin nicht an einer Maschine Ich kann jetzt ein Beispiel zusammensetzen, aber Sie sollten einen Blick auf die 'Ausrichten' Operation auf Aufzeichnungen werfen. –

Antwort

29

Ich sollte dies für Miles verlassen, aber es ist Happy Hour, wo ich herkomme und ich kann nicht widerstehen. Wie er in einem Kommentar oben erwähnt, ist der Schlüssel ops.hlist.Align, die für Datensätze (die nur spezielle hlists sind) gut funktionieren wird.

Wenn Sie eine schöne Syntax wollen, müssen Sie einen Trick wie den folgenden verwenden, um die Typ Parameter Liste mit dem Ziel (das Sie explizit angeben wollen) von der Typ Parameter Liste mit all den anderen Sachen (die Sie wollen zu entnehmen):

import shapeless._, ops.hlist.Align 

class SameFieldsConverter[T] { 
    def apply[S, SR <: HList, TR <: HList](s: S)(implicit 
    genS: LabelledGeneric.Aux[S, SR], 
    genT: LabelledGeneric.Aux[T, TR], 
    align: Align[SR, TR] 
) = genT.from(align(genS.to(s))) 
} 

def convertTo[T] = new SameFieldsConverter[T] 

Und dann:

case class A(foo: Int, bar: Int) 
case class B(bar: Int, foo: Int) 

Und dann:

scala> convertTo[B](A(12, 13)) 
res0: B = B(13,12) 

Beachten Sie, dass das Finden von Alignment-Instanzen zur Kompilierzeit für große Fallklassen teuer wird.

+3

Das ist lächerlich fantastisch, danke Travis, Miles. – Utaal

+0

Ehrfürchtig, in der Tat :) – ahjohannessen

13

Wie bemerkt @MilesSabin (gottähnlich formlos Konzept), gibt es eine align Betrieb, wird es verwendet, wie:

import ops.hlist.Align 

val aGen = LabelledGeneric[A] 
val bGen = LabelledGeneric[B] 
val align = Align[aGen.Repr, bGen.Repr] 
bGen.from(align(aGen.to(A(12, 13)))) //> res0: B = B(13,12) 

P. S. Bemerkte, dass es eine example on GitHub gibt.

+0

@TravisBrown Ja, denke, deine ist schöner und vollständiger, generische Antwort in der Tat. – DaunnC