2016-03-15 4 views
9

Ich möchte irgendwie zur Kompilierzeit den Namen eines Feldes einer Fallklasse in einem val bekommen (möglicherweise eine Singleton-typisierte Zeichenkette oder ein Symbol?).Wie erhält man den Namen eines Fallklassenfelds als String/Symbol zum Zeitpunkt der Kompilierung mit formlosem?

Etwas wie folgt aus:

import shapeless._ 
case class MyClass(field1: String, field2: Int) 
val field1Lens = lens[MyClass] >> 'field1 
// val name = field1Lens.name // it should be "field1", aka 'field1.name 

Ich muss jede Technik nicht unbedingt Linsen verwenden, die funktioniert, ist in Ordnung (etwas mit LabelledGeneric?). Ich möchte etwas haben, wo ich den Namen des Fallklassenfelds bekommen kann, ohne es separat zu spezifizieren. Auf diese Weise, wenn ich den Namen des field1 Mitglied in der Klasse umgestalten, ändert sich name entsprechend.

Natürlich ist die folgende nicht funktioniert, weil das Makro bei der Kompilierung nicht weiß, den Namen des Symbols:

val name = 'field1 
val field1Lens = lens[MyClass] >> name // can't possibly work 

ich lens[MyClass] >> name.narrow versucht, aber es funktioniert nicht, entweder

Diese ist das, was ich derzeit tue, und natürlich mag ich es nicht:

// If I change the name of the field, compilation fails 
// and I'm forced to check this line of code so I can change the string in the line below 
protected val fieldNameCheck = lens[X].someField 
val someField = "someField" 

edit: Ok, ich einen Blick auf gabriele ‚s Frage gab, und von 0 mitIch kann einen HList bekommen, der die (markierten) Schlüssel des Datensatzes enthält. Was ich jedoch brauche, ist ein bestimmtes Feld zu bekommen, nicht eine Liste, die alle enthält.

Ich versuche select zu verwenden, um einen bestimmten Schlüssel zu bekommen, aber ich habe nicht so weit

import shapeless._ 
import shapeless.syntax.singleton._ 
import shapeless.ops.record._ 

case class Foo(bar: String, baz: Boolean) 
val labl = LabelledGeneric[Foo] 
val keys = Keys[labl.Repr].apply 

// the following doesn't work 
// val bar = 'bar.narrow 
// keys.select[bar.type] 
// keys.get('bar) 
+0

Mögliche Duplikate von [Label-Werte aus einer LabelledGeneric-Instanz extrahieren] (http://stackoverflow.com/questions/27434302/extract-label-values-from-a-bellledgeeric-instance) –

+0

Ich glaube nicht, dass ich verstehe Die Motivation - es klingt so, als ob Sie die wörtliche "Bar" an einer Stelle in Ihrem Code schreiben möchten, damit Sie sie nicht an einem anderen Ort schreiben müssen, und ich bin mir nicht sicher, wie das beim Refactoring hilft. Wenn Sie Bedenken haben, dass sich die Mitgliedsnamen ändern, sind die positionellen Selektoren möglicherweise die bessere Wahl. –

+0

Lange Rede, kurzer Sinn: Serialisierungsbibliotheken.Ich möchte nicht gezwungen werden, benutzerdefinierte Serialisierer zu schreiben, um das zu tun, was die Bibliotheken bereits tun (indem ich JSON-Objekte mit Feldnamen als Schlüssel usw. schreibe), nur weil ich den Namen des Feldes an einer anderen Stelle haben muss (zB bei teilweiser Aktualisierung) auf elasticsearch). Ich schreibe die Literalleiste nur einmal, aber wenn sich die Fallklasse ändert, wird der Code trotzdem kompiliert. Ich würde eine Menge Tests brauchen, nur um sicher zu gehen, dass für jeden "Balken", den ich brauche, ein '(bar: T)' Case-Klasse-Param genau das selbe ist: eins für jedes Feld jeder Klasse. –

Antwort

6

Das Argument >> ist ein Witness, der den Mitgliedsnamen als ein Kompilierungszeitsymbol erfasst. Wenn Sie >> 'bar schreiben, wird das Symbol wörtliche implizit in einen Witness umgewandelt, die in der Regel ist das, was Sie wollen, aber Sie können auch selbst eine Beurteilung bieten:

scala> case class Foo(bar: String, baz: Boolean) 
defined class Foo 

scala> val barKey = shapeless.Witness('bar) 
barKey: shapeless.Witness.Aux[[email protected]@[Symbol,String("bar")]] = ... 

scala> shapeless.lens[Foo] >> barKey 
res0: shapeless.Lens[Foo,String] = [email protected] 

Wie ich in einem Kommentar erwähnt oben, können Sie auch interessieren in dem Positionswähler:

scala> shapeless.lens[Foo] >> shapeless.nat._1 
res1: shapeless.Lens[Foo,Boolean] = [email protected] 

oder auch nur:

scala> shapeless.lens[Foo] >> 1 
res2: shapeless.Lens[Foo,Boolean] = [email protected] 

Diese erfordern Sie nicht das Mitglied schreiben na ich irgendwo in Ihrem Code, obwohl Sie in Schwierigkeiten geraten, wenn Sie Mitglieder neu anordnen.

+0

Ich schuf schließlich eine 2-Zeilen-Methode, die tut, was ich will, es ist in meiner eigenen Antwort als Referenz. –

+1

Das war so hilfreich für etwas, was ich tue, wie auch viele andere Antworten über formlos. Vielen Dank! –

3

Ok, dank Travis' Kommentare Ich habe es funktioniert gelungen:

import shapeless._ 
case class MyClass(field1: String, field2: Int) 

def fieldName[A](fieldKey: Witness.Lt[_ <: Symbol])(implicit mkl: MkFieldLens[A, fieldKey.T]) = { 
    lens[A] >> fieldKey 
    fieldKey.value.name 
} 

println(fieldName[MyClass]('field1)) 
+0

Ich denke, Sie können ohne die Linse [A] >> fieldKey, wie es nicht zugewiesen ist. Der Compiler, der die 'MkFieldLens' -Instanz auflöst, reicht aus, um sicherzustellen, dass das Feld in der Klasse ist. –

Verwandte Themen