Is there a compelling reason why OO languages do not enforce immutability?

I’m too busy learning Haskell at the moment. I’m about 1/3 through the book and I’ve been hacking at it for…let’s see, about forever.

I’ll get to Erlang sometime within the next forty years, I’m sure.

This makes no sense to me.

If you design your object to not have to change, then make it immutable.

If you design your object to have a changing state (as the majority of programming constructs are), then copying it back and forth is retarded and does nothing to help you “parallelize” or “optimize.”

If you’re proposing to change how programmers think and design software, then ok. But simply forcing everything in Java to be immutable won’t get you there.

Besides, you don’t really want immutability to solve your parallelization problems. You want something more advanced and nuanced. Honestly, doesn’t forbidding mutability just seem like a primitive, backhanded solution?

I was thinking long the same lines as Alex, but maybe it’s because I haven’t used functional languages before.

Anyone care to show an example of doing something the old fashioned way vs the easier to parallelize way due to immutability?

For example:
arrayC = arrayB + arrayA

Old fashioned method just assign X elements per core and add in parallel. How would the immutable way look and how would it be better?

Thinking about this further, I have some business transaction code that would gain pretty much zero from being parallel. What little could be parallel simply would not be worth it. In this case it seems like there is increased complexity for little gain. Am I missing something?

Yeah, me too. The first thing that occurred to me is how do you handle multiple references to the same object? If A and B each have a reference to C, and then A needs to change the state of C, and if C is immutable so that A actually makes a C’ with the new state, then B still has the reference to C (with the old state), but A actually wants B to have the new C’ state, so then does A have to send C’ to B so that it can update its reference?

As with the array sorting example, this just seems to add a horrible new overhead without buying you anything. Not to mention being tremendously error-prone if, say, a D comes along and gets a reference to C, and then A doesn’t get updated to also tell D about the change . . .

As the majority of programming constructs are, using a language like Java, yes. And making objects immutable certainly does everything to help parallelize/make concurrent. This is precisely the reason all boxed primitive types in Java are immutable.

And it is simply incorrect to say that making objects immutable does not allow more optimizations by the compiler and runtime system. For one, the garbage collector is given a whole load of new information on when it can and can’t garbage collect: no immutable object can possible have a reference to an object younger than itself, and no immutable object can be a member of a remembered set in the garbage collector. In addition, you can use whole different styles of garbage collection that are not possible in a language that doesn’t make the distinction between immutable and mutable data painfully clear.

Though I mentioned Java in the lede, it was only for the benefit of those doing a mouseover for the thread. I want to make my own language.

No. It’s precisely the referential transparency (this is what I’m really proposing: a referentially transparent object oriented language) that allows you to parallelize Haskell code easily.

Don’t use the language, then :stuck_out_tongue:

I’m confused. You asked “is there a compelling reason why OO languages do not enforce immutability?”. And your response to my question is “don’t use the language”? But I do use OO languages (as well as others), and I thought you wanted OO languages to enforce immutability.

I was kind of hoping to better understand your position and the pros and cons of OO languages enforcing immutability.

Yes, rereading my OP, I’m unclear: I’m not proposing to retrofit Java with enforced immutability; I’m proposing a new language where objects are immutable past instantiation in order to enforce referential transparency. The purpose of the thread is to see what would go wrong in existing class-based OO languages if immutability were to be enforced. So far, most answers fall into “performance”, though Sinaijon mentioned something interesting about default behaviours that he hasn’t followed up on (hopefully he will :p).

What about the complexity issue roadfood was getting at? When multiple objects reference another object, and that other object changes, what is the mechanism to update all of those references (manual seems pretty ugly)?

Both languages that I’ve used that feature immutable objects have different schemes to handle this issue. But first off: in 90% of cases you actually do not want to do what you’re describing. There is a reason why in languages that have mutable objects, basic types are either still immutable or always copied by value instead of reference. You usually do not want a subroutine that you don’t know to just muck about with its arguments. Immutability in Erlang and Clojure just extends that to all or most types.

So what happens instead is that in Erlang you use a special key-value store “database” which may or may not be shared between processes “threads”, for all shared mutable state, or within a process you explicitly pass around a state object - which is immutable, but you can provide new versions of it to other functions.

In clojure, you get some things that are more like traditional shared references, with some nice transactional semantics for stuff you want to share, but the objects themselves are still immutable - you can only change the references. Which is good, since you do not want to have the objects themselves to just change from under you (since that would require all kinds of locking and stuff you do not want to do).

I was beginning to suspect this was the solution.

Here’s a small bit of a simulation I wrote not too long ago, how would you approach this in a functional language. It’s a distribution center simulation of the flow of objects through the building (this is just a small part of it):

A collection of containers (cartons, pallets, storage/picking locations, etc.)
A container has:

  1. A collection of other containers (pallets hold cartons, etc.)
  2. A current x,y,z location

A collection of resources (order pickers, lifts, people, conveyors, etc.)
A resource has:

  1. A collection of containers it is currently carrying
  2. A current x,y,z location
  3. A set of tasks that reference containers (e.g. move pallet from here to there which not only moves the pallet but all objects it contains)
    You can see where there are references to the same object in multiple places. Also for every movement the x,y,z location will get updated for all objects that move. When I ask how you would approach it, I’m not asking for tons of detail, just high level that gets the idea across.

I can’t help but note that that is an example of an immutable operation. None of the input arrays in this example are changed, instead it produces a new array. If you want to get into more details (looking at each element in order and add them), in clojure:


(map + [1 2 3] [2 3 4])
 => (3 5 7)

Which literally means: produce a new sequence applying + to each element of the given sequences. And in parallel (lazily):


(pmap + [1 2 3] [2 3 4])
 => (3 5 7)

Straightforward map does not work in parallel, since the overhead for doing that is pretty high if all you’re doing something simple, like adding.

Well, if you’re not actually doing this in multiple threads, you can just pass around the objects in the main loop:


(defn main-loop [containers resources] ;; define main-loop function
   ;; do stuff which just create new-containers and new-resources
   ;; possibly containing new versions of whatever those collections hold
  (mainloop new-containers new-resources)) ;; call yourself with the new versions


It may be a bit of a cop-out for a complicated program, but is the basic way of doing loops over “changing” data functionally and it shows that you can translate pretty much any mutable state program to one that doesn’t use mutable state. It just gets unwieldy after a time - if your passing around massive amounts of state, though if you’re only doing that in the mainloop it’s not so much of a problem - and it doesn’t solve any concurrency issues directly. Basically, you’re defining the problem in terms of recursion.

So you probably want some kind of actual mutable references somewhere. Be it in a key-value store (which is what erlang does) or using transactional memory (the clojure way). Transactional can be more efficient and supports some very nice idioms that are hard to do using just plain data stores, but it won’t scale easily (or at all) to multi-machine architectures. Clojure is aimed squarely at the stuff multi-threaded java is currently used for, so it doesn’t really care much about multi-machine stuff. Erlang clearly does care about that.

In any case, Rich Hickey (the designer of Clojure) has a nice threaded example of something quite similar to your simulation: an ant colony that eats food, moves stuff, and leaves pheromone trails on a grid, with all the ants working multi-threaded (via a thread pool). The way it basically works is that the grid is a vector of transactually mutable references (called simply ‘refs’ in clojure) to cells.

Each time anything is moved or placed on a cell, that gets done in a transaction, which will only succeed if all the cells referenced during the transaction are “clean” - i.e. not used by another transaction. Clojure refs guarantee automatic retries for failed transactions, and the immutability of the basic objects and its emphasis on side-effect free code makes it easy to guarantee that you can just throw away a transaction and retry in the first place, without creating a giant mess of locks.

There’s a video of him explaining it here - starts at about 79:00 and source code here

Oh, and to finally address the OP: I don’t think there is any reason to assume an OO language cannot have immutable objects, at least in the same way Clojure and Erlang support them - in other words: you still need some side-effects (obviously) and it helps to have some coordinated mutable state, but nothing about that means you cannot handle most objects immutably. The clojure implementation seems to me to be a very good example of how to deal with the intricacies of doing that efficiently.

I do question the emphasis on traditional OO in this matter, though. Sideeffect-free coding does seem to lead to a more functional approach, and taking ECMAScript as an example, trying to mix traditional OO and functional constructs can clash in a lot of ways - that’s not to say that you can’t do it, but looking at ECMAScript from a functional point of view, in there, there’s a lot of stuff that’s made unreasonably clunky, with no apparent benefit just because of - it seems - an infatuation with objects and the traditional C/Java/C++ operator/function/method divide, which just shouldn’t and doesn’t exist in modern (i.e. everything after Scheme/Common Lisp) lisps. And ruby, for example, makes it at least possible to bridge that gap - most of the time - but it’s still clunky.

Concluding: yeah, you can probably create a mostly immutable OO language. I’m just not sure it’s really the best way to go forward.

And OT question:

With all the efforts toward making programming variables immutable and functioning more like math (and that not really succeeding), have there been any effort in making math variables mutable and working more like programming?

Why would anybody do that? There’s a reason why you want to make a variable in a programme immutable, and that’s to address aliasing problems, facilitate straightforward metatheoretic reasoning about the language, and ease reasoning about programs.

I resent that remark and I think it’s ill-informed. There are obvious situations where having some immutable state is useful, especially when dealing with concurrency, but when dealing with concurrency, the attitude of “just lock the damn thing and mess it about” is short-sighted, fraught with peril and generally doesn’t lead to good performance either. In any case this has nothing at all to do with variables. Variables are just names that refer to values and truely local variables can change just fine without any complications. It’s the values that should in general not change.

I’m not aware of any effort to make it easy to change 3 into 4, and I can’t see any benefit of doing that.