2015-01-02 16 views
5

Próbuję napisać kod, który śledzi zmiany w rekordzie i stosuje je w późniejszym terminie. W dynamicznym języku zrobiłbym to, po prostu przechowując dziennik z parami List [(String, Any)], a następnie po prostu stosując je jako aktualizację oryginalnego rekordu, kiedy ostatecznie zdecyduję się zatwierdzić zmiany.Typowe generyczne aktualizacje klas przypadków w Scali

Muszę mieć możliwość introspekcji aktualizacji, więc lista funkcji aktualizacji nie jest odpowiednia.

W Scali jest to dość banalne za pomocą refleksji, jednak chciałbym wprowadzić wersję bezpieczną.

Moja pierwsza próba polegała na spróbowaniu bezkształtnym. Działa to dobrze, jeśli znamy konkretne typy.

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) 

Jednak nie mogę wymyślić, jak zrobić to ogólnie.

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

Biorąc pod uwagę sposób, w jaki to bezkształtne radzi sobie z tym, nie jestem pewien, czy to byłoby nawet możliwe?

Jeśli przyjmuję dużą liczbę elementów, jako biedną wersję mans mogę zrobić coś na wzór:

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) 
    } 
} 

Jednak nie jest to szczególnie eleganckie i wymaga wielu elementów. Prawdopodobnie mógłbym napisać własne makro, aby sobie z tym poradzić, ale wydaje się dość powszechne - czy istnieje sposób radzenia sobie z tym z Shapeless lub podobną biblioteką makr?

+0

Czy spojrzałeś na elementy optyczne (soczewki, pryzmaty itp.)? – rightfold

+0

Jeśli poprawnie rozumiem soczewki, pozwalają mi jawnie zaktualizować rekord, ale nie mogę rejestrować metadanych dotyczących zmian, które chcę później zastosować? –

+3

Myślę, że bezkształtny zapewnia większość potrzebnych części. Możesz przejrzeć przykłady bezpiecznej delty i klasy przypadków a la carte w najnowszej bezkształtnej migawce 2.1.0. Poza tym twoje pytanie nie jest wystarczająco precyzyjne, abym mógł odpowiedzieć na to bez pisania wszystkiego dla ciebie ;-) –

Odpowiedz

1

Co powiesz na wykorzystanie całej instancji klasy jako aktualizacji?

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)) 
} 

myślę, jeśli chcesz naprawdę typ-bezpieczne rozwiązanie, to jest najczystszym i najbardziej czytelne, a także, w miarę możliwości najmniejszy ból do wdrożenia, jak nie trzeba zajmować się Records, soczewki i poszczególne deskryptory pól, po prostu ContactUpdate(name="foo") tworzy aktualizację, a updates.map(Contact.update(target, _)) stosuje je wszystkie kolejno.

Powiązane problemy