11
votes

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.

2
Here's a related question: stackoverflow.com/questions/13803843/…Jan Hettich

2 Answers

13
votes

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.

4
votes

I think the short answer is most likely: Yes, immutable data structures are far more usable and efficient than you realize.

The question you've posed is a bit ambiguous because the answer depends less on the motor you've described than on the software system that you haven't described. The great mistake of how OOP is always taught, in my opinion, is recommending bottom-up design of "domain" classes prior to considering how the classes will be used. Maybe your system even needs more than one data structure holding the same information about a motor in different ways.

The "old way" of doing OOP in Java or C# using classes to encapsulate mutable state seems to fit the motor example very well.

The "new way" (arguably), in support of multithreaded systems, is to encapsulate mutable state within actors. An actor that represents the current state of a motor would be mutable. But if you were to take a "snapshot" of the motor's state and pass that information to another actor, the message needs to be immutable.

In that [immutable] case, would 'speed' be represented internally as a 'val' instead of a 'var', and the 'setSpeed' method return a new instance of the class?

Yes, but you don't actually have to write that method if you use a case class. Suppose you have a class defined as case class Motor(speed: Speed, rpm: Int, mass: Mass, color: Color). Using the copy method, you could write something like motor2 = motor1.copy(rpm = 3500, speed = 88.mph).