Scala – When to use mutable vs immutable classes in Scala

immutabilityscala

Much is written about the advantages of immutable state, but are there common cases in Scala where it makes sense to prefer mutable classes? (This is a Scala newbie question from someone with a background in "classic" OOP design using mutable classes.)

For something trivial like a 3-dimensional Point class, I get the advantages of immutability. But what about something like a Motor class, which exposes a variety of control variables and/or sensor readings? Would a seasoned Scala developer typically write such a class to be immutable? In that case, would 'speed' be represented internally as a 'val' instead of a 'var', and the 'setSpeed' method return a new instance of the class? Similarly, would every new reading from a sensor describing the motor's internal state cause a new instance of Motor to be instantiated?

The "old way" of doing OOP in Java or C# using classes to encapsulate mutable state seems to fit the Motor example very well. So I'm curious to know if once you gain experience using the immutable-state paradigm, you would even design a class like Motor to be immutable.

Best Answer

I'll use a different, classic, OO modeling example: bank accounts.

These are used in practically every OO course on the planet, and the design you usually end up with is something like this:

class Account(var balance: BigDecimal) {
  def transfer(amount: BigDecimal, to: Account): Unit = { 
    balance -= amount
    to.balance += amount
  }
}

IOW: the balance is data, and the transfer is an operation. (Note also that the transfer is a complex operation involving multiple mutable objects, which however should be atomic, not complex … so you need locking etc.)

However, that is wrong. That's not how banking systems are actually designed. In fact, that's not how actual real-world (physical) banking works, either. Actual physical banking and actual banking systems work like this:

class Account(implicit transactionLog: TransactionLog) {
  def balance = transactionLog.reduceLeft(_ + _)
}

class TransactionSlip(from: Account, to: Account, amount: BigDecimal)

IOW: the balance is an operation and the transfer is data. Note that everything here is immutable. The balance is just a left fold of the transaction log.

Note also that we didn't even end up with a purely functional, immutable design as an explicit design goal. We just wanted to model the banking system correctly and ended up with a purely functional, immutable design by coincidence. (Well, it's actually not by coincidence. There's a reason why real-world banking works that way, and it has the same benefits as it has in programming: mutable state and side-effects make systems complex and confusing … and in banking that means money disappearing.)

The point here is that the exact same problem can be modeled in very different ways, and depending on the model, you might up with something which is trivial to make purely immutable or very hard.

Related Topic