2015-01-02 15 views
5

Sto tentando di scrivere del codice che tiene traccia delle modifiche a un record e le applica in un secondo momento. In un linguaggio dinamico lo farei semplicemente mantenendo un log di coppie List [(String, Any)] e poi semplicemente applicandoli come aggiornamento al record originale quando alla fine decido di eseguire il commit delle modifiche.Aggiornamenti generici sulla classe del caso sicuro in Scala

Devo essere in grado di analizzare gli aggiornamenti, quindi un elenco di funzioni di aggiornamento non è appropriato.

In Scala questo è piuttosto banale con la riflessione, tuttavia mi piacerebbe implementare una versione sicura per tipo.

Il mio primo tentativo è stato provare senza forma. Funziona bene se conosciamo tipi specifici.

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

case class Person(name:String, age:Int) 
val bob = Person("Bob", 31) 
val gen = LabelledGeneric[Person] 

val updated = gen.from(gen.to(bob) + ('age ->> 32)) 

// Result: Person("Bob", 32) 

Tuttavia non riesco a capire come rendere questo lavoro genericamente.

trait Record[T] 
    def update(???):T 
} 

dato il modo in maniglie informi questo, io non sono sicuro se questo sarebbe anche essere possibile?

Se accetto un sacco di carte, come una versione povera pot ho potuto fare qualcosa in linea con quanto segue.

object Contact { 
    sealed trait Field[T] 
    case object Name extends Field[String] 
    case object Age extends Field[Int] 
} 

// A typeclass would be cleaner, but too verbose for this simple example. 
case class Contact(...) extends Record[Contact, Contact.Field] { 
    def update[T](field:Contact.Field[T], value:T) = field match {   
     case Contact.Name => contact.copy(name = value) 
     case Contact.Age => contact.copy(age = value) 
    } 
} 

Tuttavia, questo non è particolarmente elegante e richiede un sacco di piastre. Probabilmente potrei scrivere la mia macro per gestirlo, tuttavia sembra una cosa abbastanza comune - c'è un modo per gestirlo già con Shapeless o una libreria macro simile?

+0

Avete dato un'occhiata all'ottica (lenti, prismi, ecc.)? – rightfold

+0

Se ho compreso correttamente gli obiettivi, mi consentono di aggiornare esplicitamente il record, ma non di registrare i metadati relativi alle modifiche che voglio applicare in seguito? –

+3

Penso che shapeless fornisca la maggior parte delle parti necessarie. Puoi dare un'occhiata al tipo sicuro delta e alle classi di casi con esempi a la carte nell'ultima istantanea di shapeless 2.1.0. Oltre a questo la tua domanda non è abbastanza precisa da poter rispondere senza scrivere tutto per te ;-) –

risposta

1

Come utilizzare l'intera istanza della classe come aggiornamento?

case class Contact(name: String, age: Int) 
case class ContactUpdate(name: Option[String] = None, age: Option[Int] = None) 

object Contact { 
    update(target: Contact, delta: ContactUpdate) = Contact(
     delta.name.getOrElse(target.name) 
     target.age.getOrElse(delta.age) 
    ) 
} 
// also, optionally this: 
object ContactUpdate { 
    apply(name: String) = ContactUpdate(name = Option(name)) 
    apply(age: Int) = ContactUpdate(age = Option(age)) 
} 

penso, se si vuole veramente la soluzione type-safe, questo è il più pulito e più leggibile, e anche, eventualmente, meno dolore per implementare, in quanto non è necessario per affrontare Records, lenti e singoli descrittori di campo, solo ContactUpdate(name="foo") crea un aggiornamento e updates.map(Contact.update(target, _)) li applica tutti in sequenza.

Problemi correlati