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

后端 未结 5 1446
难免孤独
难免孤独 2020-11-30 17:58

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

相关标签:
5条回答
  • 2020-11-30 18:10

    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
    0 讨论(0)
  • 2020-11-30 18:13

    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.

    0 讨论(0)
  • 2020-11-30 18:17
    existingPersona.copy(sentMessages = existingPersona.sentMessages + newMessage)
    
    0 讨论(0)
  • 2020-11-30 18:19

    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
    
    0 讨论(0)
  • 2020-11-30 18:20

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

    val newPersona = existingPersona.copy(sentMessages = 
                       existingPersona.sentMessages + newMessage)
    
    0 讨论(0)
提交回复
热议问题