Let\'s say I have a case class that represents personas, people on different social networks. Instances of that class are fully immutable, and are held in immutable collecti
Consider using lens
in Shapeless
library:
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))
Moreover, in case you have nested case classes, the getter
and setter
methods can be a bit tedious to compose. It will be a good chance to simplify by using lens library.
Please also refer to:
I didn't want to include a big library to do complex lenses that let you set values deep in nested case classes. It turns out it is just a few lines of code in the scalaz library:
/** 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
}
You can then create lenses that set deeply nested values far easier than using the built in copy feature. Here is a link to a big set if complex lenses that that my library uses to set heavily nested values.
existingPersona.copy(sentMessages = existingPersona.sentMessages + newMessage)
Since 2.8, Scala case classes have a copy
method that takes advantage of named/default params to work its magic:
val newPersona =
existingPersona.copy(sentMessages = existing.sentMessages + newMessage)
You can also create a method on Persona
to simplify usage:
case class Persona(
svcName : String,
svcId : String,
sentMsgs : Set[String]
) {
def plusMsg(msg: String) = this.copy(sentMsgs = this.sentMsgs + msg)
}
then
val newPersona = existingPersona plusMsg newMsg
case class
comes with a copy
method that is dedicated exactly to this usage:
val newPersona = existingPersona.copy(sentMessages =
existingPersona.sentMessages + newMessage)