2016-03-15 12 views
9

Mi piacerebbe ottenere in qualche modo al momento della compilazione il nome di un campo di una classe in un caso val (forse una stringa o un simbolo Singleton-digitato?).Come ottenere il nome di un campo di classe case come stringa/simbolo in fase di compilazione usando shapeless?

Qualcosa di simile a quanto segue:

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 

non devo necessariamente utilizzare le lenti, qualsiasi tecnica che funziona va bene (qualcosa con LabelledGeneric?). Mi piacerebbe avere qualcosa in cui posso ottenere il nome del campo della classe case senza specificarlo separatamente. In questo modo, se refactoring il nome del membro field1 nella classe, name modifiche di conseguenza.

Ovviamente il seguente non funziona perché la macro non conosce al momento della compilazione del nome del simbolo:

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

ho provato lens[MyClass] >> name.narrow ma non funziona nemmeno

Questo è quello che sto facendo attualmente, e naturalmente non mi piace:

// 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, ho dato uno sguardo a gabriele s' domanda, e utilizzando Keys Sono in grado di ottenere una lista HL contenente i tasti (taggati) del record. Quello che mi serve però è ottenere un campo specifico, non un elenco che li contiene tutti.

che sto cercando di utilizzare select per ottenere una chiave particolare, ma non sono riuscito finora

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

Possibile duplicato di [Estrai valori etichetta da un'istanza LabelledGeneric] (http://stackoverflow.com/questions/27434302/extract-label-values-from-a-labelledgeneric-instance) –

+0

Non credo di capire la motivazione: sembra che tu voglia scrivere il letterale "bar" in un punto del tuo codice in modo da non doverlo scrivere in un altro posto, e non sono sicuro di come ciò possa aiutare con il refactoring. Se sei preoccupato che i nomi dei membri cambino, i selettori posizionali potrebbero essere una scelta migliore. –

+0

long story short: librerie di serializzazione.Non voglio essere costretto a scrivere serializzatori personalizzati per fare quello che le librerie già fanno (scrivendo oggetti JSON usando nomi di campi come chiavi, ecc.) Solo perché ho bisogno di avere il nome del campo in qualche altro posto (es. Quando si aggiorna parzialmente su elasticsearch). Scrivo solo una volta la barra letterale, ma se la classe case cambia, il codice viene comunque compilato. Avrei bisogno di un sacco di test solo per essere sicuro che per ogni ''bar' ho bisogno che ci sia un parametro della classe case' (bar: T) 'chiamato esattamente lo stesso: uno per ogni campo di ogni classe. –

risposta

6

L'argomento >> è un Witness che acquisisce il nome membro come simbolo di compilazione. Quando si scrive >> 'bar, il simbolo letterale è implicitamente convertito in un Witness, che di solito è quello che si vuole, ma si può anche fornire uno voi stessi:

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] 

Come detto in un commento di cui sopra, si può anche essere interessati nei selettori di posizione:

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

o anche solo:

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

Questi non richiedono di scrivere il membro na ovunque nel tuo codice, anche se ti troverai nei guai se riorganizzi i membri.

+0

Alla fine ho creato un metodo a 2 righe che fa ciò che voglio, è nella mia risposta di riferimento. –

+1

Questo è stato molto utile per qualcosa che sto facendo, così come molte altre tue risposte su informe. Grazie! –

3

Ok, grazie ai commenti di Travis' ho capito di lavoro:

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

Penso che tu possa fare a meno dell'obiettivo [A] >> fieldKey perché non è assegnato. Il compilatore che risolve l'istanza di 'MkFieldLens' è sufficiente per assicurarsi che il campo si trovi sulla classe. –

Problemi correlati