How to clone a case class instance and change just one field in Scala?

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

existingPersona.copy(sentMessages = existingPersona.sentMessages + newMessage)

case classcomes with a copy method that is dedicated exactly to this usage:

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

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:

  • Shapeless Github / Boilerplate-free lenses for arbitrary case classes
  • Quicklens Github
  • Lens in scala

Tags:

Scala