Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala "update" immutable object best practices

With a mutable object I can write something like

var user = DAO.getUser(id)
user.name = "John"
user.email ="[email protected]"
// logic on user

If user is immutable then I need to clone\copy it on every change operation.

I know a few ways to perform this

  1. case class copy
  2. method (like changeName) that creates a new object with the new property

What is the best practice?

And one more question. Is there any existing technique to get "changes" relative to the original object(for example to generate update statement)?

like image 637
sh1ng Avatar asked Jul 09 '13 13:07

sh1ng


People also ask

Can immutable objects be changed at runtime?

In object-oriented and functional programming, an immutable object (unchangeable object) is an object whose state cannot be modified after it is created. This is in contrast to a mutable object (changeable object), which can be modified after it is created.

Can you change values of an immutable object?

The immutable objects are objects whose value can not be changed after initialization. We can not change anything once the object is created. For example, primitive objects such as int, long, float, double, all legacy classes, Wrapper class, String class, etc. In a nutshell, immutable means unmodified or unchangeable.

Should you use immutable objects?

Immutable objects offer a number of advantages for building reliable applications. As we don't need to write defensive or protective code to keep application state consistent, our code can be simpler, more concise, and less error-prone than when we define mutable objects.

Can we use immutable objects in multithreading?

Immutable objects are useful in multithreaded applications because they can be safely accessed by several threads concurrently, without the need for locking or other synchronization.


1 Answers

Both ways you've mentioned belongs to functional and OO paradigms respectively. If you prefer functional decomposition with abstract data type, which, in Scala, is represented by case classes, then choose copy method. Using mutators is not a good practice in my option, cause that will pull you back to Java/C#/C++ way of life.

On the other hand making ADT case class like

case class Person(name: String, age: String)

is more consise then:

class Person(_name: String, _age: String) {
  var name = _name
  var age = _a

  def changeName(newName: String): Unit = { name = newName }
  // ... and so on
}

(not the best imperative code, can be shorter, but clear).

Of cause there is another way with mutators, just to return a new object on each call:

class Person(val name: String, 
             val age: String) {      
  def changeName(newName: String): Unit = new Person(newName, age)
  // ... and so on
}

But still case class way is more consise.

And if you go futher, to concurrent/parallel programming, you'll see that functional consept with immutable value are much better, then tring to guess in what state your object currently are.

Update

Thanks to the senia, forgot to mention two things.

Lenses
At the most basic level, lenses are sort of getters and setters for immutable data and looks like this:

case class Lens[A,B](get: A => B, set: (A,B) => A) {
  def apply(a: A) = get(a)  
  // ...
}

That is it. A lens is a an object that contains two functions: get and set. get takes an A and returns a B. set takes an A and B and returns a new A. It’s easy to see that the type B is a value contained in A. When we pass an instance to get we return that value. When we pass an A and a B to set we update the value B in A and return a new A reflecting the change. For convenience the get is aliased to apply. There is a good intro to Scalaz Lens case class

Records
This one, ofcause, comes from the shapeless library and called Records. An implementation of extensible records modelled as HLists of associations. Keys are encoded using singleton types and fully determine the types of their corresponding values (ex from github):

object author  extends Field[String]
object title   extends Field[String]
object price   extends Field[Double]
object inPrint extends Field[Boolean]

val book =
  (author -> "Benjamin Pierce") ::
  (title  -> "Types and Programming Languages") ::
  (price  ->  44.11) ::
  HNil

// Read price field
val currentPrice = book.get(price)  // Inferred type is Double
currentPrice == 44.11

// Update price field, relying on static type of currentPrice
val updated = book + (price -> (currentPrice+2.0))

// Add a new field
val extended = updated + (inPrint -> true)
like image 60
4lex1v Avatar answered Oct 28 '22 15:10

4lex1v