11

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.

Jan Hettich
  • 9,146
  • 4
  • 35
  • 34
  • Here's a related question: http://stackoverflow.com/questions/13803843/what-is-the-best-way-to-manage-mutable-state – Jan Hettich Sep 30 '13 at 03:36

2 Answers2

13

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.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 1
    I think you have the beginnings of an interesting answer here, but the example could benefit from a more lucid explanation of how the changing state is modeled in the functional representation. How does the account balance "change"? – Aaron Novstrup Sep 30 '13 at 18:12
  • 2
    It doesn't, that's the point. It's a function, it's computed every time you look at it. – Jörg W Mittag Sep 30 '13 at 23:56
  • 5
    I guess the point I was getting at is that account balances *do* change in real-world banking. That change must be modeled somewhere, and it's not clear from your answer precisely where that would be. When a transfer occurs, does my `Account` instance get replaced with a new one that has an updated `TransactionLog`? What keeps my `TransactionLog` in sync with the person's log on the other side of the transaction? or do all accounts share a log? Is the log itself mutable? These questions get to the heart of the complexity that your answer seems to paper over. – Aaron Novstrup Oct 01 '13 at 00:24
  • I don't think `foldLeft`can be used like that, can it? Did you mean to write `reduceLeft`? – 0x6C38 Jul 25 '16 at 22:59
4

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).

Chris Martin
  • 30,334
  • 10
  • 78
  • 137