web-dev-qa-db-fra.com

Comment cloner une instance de classe de cas et modifier un seul champ dans Scala?

Disons que j'ai une classe de cas qui représente des personas, des personnes sur différents réseaux sociaux. Les instances de cette classe sont totalement immuables et font partie de collections immuables, qui seront éventuellement modifiées par un acteur Akka.

Maintenant, j'ai une classe de cas avec de nombreux champs et je reçois un message indiquant que je dois mettre à jour l'un des champs, quelque chose comme ceci:

case class Persona(serviceName  : String,
                   serviceId    : String,
                   sentMessages : Set[String])

// Somewhere deep in an actor
val newPersona = Persona(existingPersona.serviceName,
                         existingPersona.serviceId,
                         existingPersona.sentMessages + newMessage)

Notez que je dois spécifier tous les champs, même si un seul change. Existe-t-il un moyen de cloner l’utilisateur existant et de ne remplacer qu’un seul champ sans spécifier tous les champs qui ne changent pas? Puis-je écrire cela comme un trait et l'utiliser pour toutes mes classes de cas?

Si Persona était une instance de type Map, ce serait facile à faire.

203

case classcomes avec une méthode copy dédiée à cet usage:

val newPersona = existingPersona.copy(sentMessages = 
                   existingPersona.sentMessages + newMessage)
306
Nicolas

Depuis la 2.8, Scala ont une méthode copy qui tire parti des paramètres nommés/par défaut pour exploiter sa magie:

val newPersona =
  existingPersona.copy(sentMessages = existing.sentMessages + newMessage)

Vous pouvez également créer une méthode sur Persona pour simplifier l'utilisation:

case class Persona(
  svcName  : String,
  svcId    : String,
  sentMsgs : Set[String]
) {
  def plusMsg(msg: String) = this.copy(sentMsgs = this.sentMsgs + msg)
}

ensuite

val newPersona = existingPersona plusMsg newMsg
45
Kevin Wright
existingPersona.copy(sentMessages = existingPersona.sentMessages + newMessage)
10

Je ne voulais pas inclure une grande bibliothèque pour faire des objectifs complexes qui vous permettent de définir des valeurs profondes dans des classes de cas imbriquées. Il s'avère que ce ne sont que quelques lignes de code dans la bibliothèque scalaz:

  /** http://stackoverflow.com/a/5597750/329496 */
  case class Lens[A, B](get: A => B, set: (A, B) => A) extends ((A) => B) with Immutable {
    def apply(whole: A): B = get(whole)

    def mod(a: A, f: B => B) = set(a, f(this (a)))

    def compose[C](that: Lens[C, A]) = Lens[C, B](
      c => this(that(c)),
      (c, b) => that.mod(c, set(_, b))
    )

    def andThen[C](that: Lens[B, C]) = that compose this
  }

Vous pouvez ensuite créer des objectifs qui définissent des valeurs profondément imbriquées beaucoup plus facilement que d'utiliser la fonction de copie intégrée. Voici un lien vers un grand ensemble d'objectifs complexes que ma bibliothèque utilise pour définir des valeurs fortement imbriquées.

0
simbo1905

Pensez à utiliser lens dans la bibliothèque Shapeless:

import shapeless.lens

case class Persona(serviceName  : String,
                   serviceId    : String,
                   sentMessages : Set[String])
// define the lens
val messageLens = lens[Persona] >> 'sentMessages 

val existingPersona = Persona("store", "Apple", Set("iPhone"))

// When you need the new copy, by setting the value,
val newPersona1 = messageLens.set(existingPersona)(Set.empty)
// or by other operation based on current value.
val newPersona2 = messageLens.modify(existingPersona)(_ + "iPad")

// Results:
// newPersona1: Persona(store,Apple,Set())
// newPersona2: Persona(store,Apple,Set(iPhone, iPad))

De plus, si vous avez imbriqué, les classes getter et setter Les méthodes peuvent être un peu fastidieuses à composer. Ce sera une bonne occasion de simplifier en utilisant la bibliothèque de lentilles.

Veuillez également vous référer à:

0
Kaihua