51

In most OOP languages, objects are generally mutable with a limited set of exceptions (like e.g. tuples and strings in python). In most functional languages, data is immutable.

Both mutable and immutable objects bring a whole list of advantages and disadvantages of their own.

There are languages that try to marry both concepts like e.g. scala where you have (explicitly declared) mutable and immutable data (please correct me if I am wrong, my knowledge of scala is more than limited).

My question is: Does complete (sic!) immutability -i.e. no object can mutate once it has been created- make any sense in an OOP context?

Are there designs or implementations of such a model?

Basically, are (complete) immutability and OOP opposites or orthogonal?

Motivation: In OOP you normally operate on data, changing (mutating) the underlying information, keeping references between those objects. E.g. an object of class Person with a member father referencing another Person object. If you change the name of the father, this is immediately visible to the child object with no need for update. Being immutable you would need to construct new objects for both father and child. But you would have a lot less kerfuffle with shared objects, multi-threading, GIL, etc.

18
  • 3
    Immutability can be simulated in an OOP language, by only exposing object access points as methods or read-only properties that do not mutate the data. Immutability works the same in OOP languages as it does in any functional language, except that you may be missing some functional language features. Commented Mar 17, 2014 at 19:12
  • 7
    Mutability is not a property of OOP languages like C# and Java, nor is immutability. You specify mutability or immutability by the way you write the class. Commented Mar 17, 2014 at 19:17
  • 10
    Your presumption seems to be that mutability is a core feature of object-orientation. It isn't. Mutability is simply a property of objects or values. Object-orientation encompasses a number of intrinsic concepts (encapsulation, polymorphism, inheritance, etc.) that have little or nothing to do with mutation, and you would still derive the benefits of those features. even if you made everything immutable. Commented Mar 17, 2014 at 19:27
  • 2
    @MichaelT The question is not about making specific things mutable, it's about making all things immutable.
    – user7043
    Commented Mar 17, 2014 at 19:41
  • 2

7 Answers 7

51

OOP and immutability are almost completely orthogonal to each other. However, imperative programming and immutability are not.

OOP can be summarized by two core features:

  • Encapsulation: I will not access the contents of objects directly, but rather communicate via a specific interface (“methods”) with this object. This interface can hide internal data from me. Technically, this specific to modular programming rather than OOP. Accessing data via a defined interface is roughly equivalent to an abstract data type.

  • Dynamic Dispatch: When I call a method on an object, the executed method will be resolved at run time. (E.g. in class-based OOP, I might call a size method on a IList instance, but the call might be resolved to an implementation in a LinkedList class). Dynamic dispatch is one way to allow polymorphic behavior.

Encapsulation makes less sense without mutability (there is no internal state that could be corrupted by external meddling), but it still tends to make abstractions easier even when everything is immutable.

An imperative program consists of statements which are executed sequentially. A statement has side effects like changing the state of the program. With immutability, state cannot be changed (of course, a new state could be created). Therefore, imperative programming is fundamentally incompatible with immutability.

It now happens that OOP has historically always been connected with imperative programming (Simula is based on Algol), and all mainstream OOP languages have imperative roots (C++, Java, C#, … are all rooted in C). This does not imply that OOP itself would be imperative or mutable, this just means that the implementation of OOP by these languages allows mutability.

7
  • 2
    Thank you very much, especially for the definition of the two core features. Commented Mar 17, 2014 at 19:58
  • Dynamic Dispatch is not a core feature of OOP. Neither really is Encapsulation (as you already admitted).
    – OrangeDog
    Commented Mar 18, 2014 at 13:25
  • 5
    @OrangeDog Yes, there is no universally accepted definition of OOP, but I needed a definition to work with. So I picked something that is as close to the truth as I could get without writing a whole dissertation about it. However, I do regard dynamic dispatch as the single major distinguishing feature of OOP from other paradigms. Something that looks like OOP but actually has all calls resolved statically is really just modular programming with ad-hoc polymorphism. Objects are a pair of methods and data, and are as such equivalent to closures.
    – amon
    Commented Mar 18, 2014 at 13:36
  • 2
    "Objects are a pairing of methods and data" would do. All you need is something that has nothing to do with immutability.
    – OrangeDog
    Commented Mar 18, 2014 at 13:55
  • 3
    @CodeYogi Data hiding is the most common kind of encapsulation. However, the way data is stored internally by an object is not the only implementation detail that should be hidden. It's equally important to hide how the public interface is implemented, e.g. whether I use any helper methods. Such helper methods should also be private, generally speaking. So to summarize: encapsulation is a principle, whereas data hiding is an encapsulation technique.
    – amon
    Commented May 9, 2016 at 17:11
31

Note, there's a culture among object oriented programmers where people assume if you're doing OOP that most of your objects will be mutable, but that's a separate issue from whether OOP requires mutability. Also, that culture seems to be slowly changing toward more immutability, due to people's exposure to functional programming.

Scala is a really good illustration that mutability isn't required for object-orientation. While Scala supports mutability, its use is discouraged. Idiomatic Scala is very much object oriented and also almost entirely immutable. It mostly allows mutability for compatibility with Java, and because in certain circumstances immutable objects are inefficient or convoluted to work with.

Compare a Scala list and a Java list, for example. Scala's immutable list contains all the same object methods as Java's mutable list. More, in fact, because Java uses static functions for operations like sort, and Scala adds functional-style methods like map. All the hallmarks of OOP—encapsulation, inheritance, and polymorphism—are available in a form familiar to object-oriented programmers and used appropriately.

The only difference you'll see is when you change the list you get a new object as a result. That often requires you to use different design patterns than you would with mutable objects, but it doesn't require you to abandon OOP altogether.

2
  • Well yes, but the question is exactly about those patterns you mention in the last paragraph. Give examples maybe?
    – Flavius
    Commented Dec 6, 2019 at 5:13
  • I guess that depends on the definition you use for OOP, but such a list you describe is closer to functional than oop. All the methods on it are functional style methods, but it has a object oriented data structure. I personally believe there are 3 types: object oriented, functional object oriented and pure functional. I'll argue that mutability is a core feature of pure OOP, not having it, you enter a realm that's closer to functional.
    – Ced
    Commented Sep 4, 2022 at 10:42
18

Immutability can be simulated in an OOP language, by only exposing object access points as methods or read-only properties that do not mutate the data. Immutability works the same in OOP languages as it does in any functional language, except that you may be missing some functional language features.

Your presumption seems to be that mutability is a core feature of object-orientation. But mutability is simply a property of objects or values. Object-orientation encompasses a number of intrinsic concepts (encapsulation, polymorphism, inheritance, etc.) that have little or nothing to do with mutation, and you would still derive the benefits of those features, even if you made everything immutable.

Not all functional languages require immutability, either. Clojure has a specific annotation that allows types to be mutable, and most of the "practical" functional languages have a way to specify mutable types.

A better question to ask might be "Does complete immutability make sense in imperative programming?" I'd say the obvious answer to that question is no. To achieve complete immutability in imperative programming, you would have to forego things like for loops (since you would have to mutate a loop variable) in favor of recursion, and now you're essentially programming in a functional manner anyway.

11
  • Thank you. Could you please elaborate a bit your last paragraph ("obvious" might be a bit subjective). Commented Mar 17, 2014 at 19:37
  • Already did.... Commented Mar 17, 2014 at 19:37
  • 1
    @Hyperboreus There are many ways to achieve polymorphism. Subtyping with dynamic dispatch, static ad-hoc polymorphism (aka. function overloading) and parametric polymorphism (aka generics) are the most common ways to do that, and all ways have their strengths and weaknesses. Modern OOP languages combine all these three ways, whereas Haskell primarily relies on parametric polymorphism and ad-hoc polymorphism.
    – amon
    Commented Mar 17, 2014 at 20:11
  • 3
    @RobertHarvey You say that you need mutability because you need to loop (otherwise you have to use recursion). Before I started using Haskell two years ago, I thought I needed mutable variables, too. I'm just saying there's other ways to "loop" (map, fold, filter, etc.). Once you take looping off the table, why else would you need mutable variables?
    – cimmanon
    Commented Mar 18, 2014 at 16:16
  • 1
    @RobertHarvey But this is exactly the point of programming languages: What is exposed to you and not what is happening under the hood. The latter is the responsability of the compiler or interpreter, not of the application developper. Otherwise back to assembler. Commented Mar 18, 2014 at 16:55
6

It's often useful to categorize objects as encapsulating values or entities, with the distinction being that if something is a value, code which holds a reference to it should never see its state change in any fashion which the code itself did not initiate. By contrast, code which holds a reference to an entity may expect it to change in ways beyond the reference-holder's control.

While it's possible to use encapsulate value using objects of mutable or immutable types, an object can only behave as a value if at least one of the following conditions applies:

  1. No reference to the object will ever be exposed to anything that might change the state encapsulated therein.

  2. The holder of at least one of the references to the object knows all the uses to which any extant reference might get put.

Since all instances of immutable types automatically satisfy the first requirement, using them as values is easy. Ensuring that either requirement is met when using mutable types is, by contrast, much more difficult. Whereas references to immutable types can be freely passed around as a means of encapsulating the state encapsulated therein, passing around state stored in mutable types requires either constructing immutable wrapper objects, or else copying the state encapsulated by privately-held objects into other objects which are either supplied by or constructed for the recipient of the data.

Immutable types work very well for passing values, and are often at least somewhat usable for manipulating them. They are not so good, however, at handling entities. The closest thing one can have to an entity in a system with purely-immutable types is a function which, given the state of the system, will report that attributes of some part thereof, or produce a new system-state instance which is like a supplied one except for some particular part thereof which will be different in some selectable fashion. Further, if the purpose of an entity is to interface some code to something that exists in the real world, it may be impossible for the entity to avoid exposing mutable state.

For example, if one receives some data over a TCP connection, one could produce a new "state of the world" object which includes that data in its buffer without affecting any references to the old "state of the world", but old copies of the world state which doesn't include the last batch of data will be defective and shouldn't be used since they will no longer match the state of the real-world TCP socket.

0
4

In c# some types are immutable like string.

This seems to furthermore suggest that the choice has been strongly considered.

For sure it's really performance demanding to use immutable types if you have to modify that type hundred of thousand of times. That's the reason why it's suggested to use the StringBuilder class instead of the string class in this cases.

I've made an experiment with a profiler and using the immutable type is really more CPU and RAM demanding.

It's also intuitive if you consider that for modifying just one letter in a string of 4000 characters you have to copy every char in another area of the RAM.

8
  • 6
    Frequently modifying immutable data does not need to be catastrophically slow as with repeated string concatenation. For virtually all kinds of data/use cases, an efficient persistent structure can be (often already has been) invented. Most of those have roughly equal performance, even if the constant factors are sometimes worse.
    – user7043
    Commented Mar 17, 2014 at 19:40
  • @delnan I also think that the last paragraph of the answer is more about an implementational detail than about (im)mutability. Commented Mar 17, 2014 at 19:41
  • @Hyperboreus: do you think I should delete that part? But how can a string change if it is immutable? I mean.. in my opinion, but for sure I can be wrong, that could be the main reason why object are not immutable.
    – Revious
    Commented Mar 17, 2014 at 19:42
  • 1
    @Revious By no means. Leave it, so it causes discussion and more interesting opinions and points of view. Commented Mar 17, 2014 at 19:44
  • 1
    @Revious Yes, reading would be slower, though not as slow as changing a string (the traditional representation). A "string" (in the representation I'm talking about) after 1000 modifies would be just like a freshly created string (modulo contents); no useful or widely used persistent data structure degrades in quality after X operations. Memory fragmentation is not a serious problem (you'd have many allocations, yes, but fragmentation is well a non-issue in modern garbage collectors)
    – user7043
    Commented Mar 17, 2014 at 21:19
0

Complete immutability of everything doesn't make much sense in OOP, or most other paradigms for that matter, for one very big reason:

Every useful program has side effects.

A program that does not cause anything to change, is worthless. You may as well not have even run it, as the effect will be identical.

Even if you think you're not changing anything, and are simply summing up a list of numbers you somehow received, consider that you need to do something with the result -- whether you print it to standard output, write it to a file, or wherever. And that involves mutating a buffer and changing the state of the system.

It can make a lot of sense to restrict mutability to the parts that need to be able to change. But if absolutely nothing needs to change, then you're not doing anything worth doing.

9
  • 4
    I fail to see how your answer relates to the question as I didn't address pure functional languages. Take erlang for example: Immutable data, no destructive assignment, no hazzle about side effects. Also you have state in a functional language, only that the state "flows" through the functions, unlike the functions operating on the state. The state changes, but it doesn't mutate in place, but a future state replaces the current state. Immutability is not about whether a memory buffer is changed or not, it is about whether these mutations are visible from the outside. Commented Mar 17, 2014 at 23:32
  • And a future state replaces the current one how, exactly? In an OO program, that state is a property of an object somewhere. Replacing the state requires changing an object (or replacing it with another, which requires a change to the objects that refer to it (or replacing it with another, which...eh. You get the point)). You might come up with some type of monadic hack, where every action ends up creating a whole new application...but even then, the current state of the program has to be recorded somewhere.
    – cHao
    Commented Mar 17, 2014 at 23:58
  • 8
    -1. This is incorrect. You're confusing side effects with mutation, and while they're often treated the same by functional languages, they are different. Every useful program has side effects; not every useful program has mutation. Commented Mar 18, 2014 at 1:47
  • @Michael: When it comes to OOP, mutation and side effects are so intertwined that they can not be realistically separated. If you have no mutation, then you can not have side effects without massive amounts of hackery.
    – cHao
    Commented Mar 18, 2014 at 2:00
-1

I think it depends on whether your definition of OOP is that it uses a message-passing style.

Pure functions don't have to mutate anything because they return values which you can store in new variables.

var brandNewVariable = pureFunction(foo);

With message passing style, you tell an object to store new data instead of asking it what new data you should store in a new variable.

sameOldObject.changeMe(foo);

It's possible to have objects and not mutate them, by making its methods pure functions that happen to live on the inside of the object instead of outside.

var brandNewVariable = nonMutatingObject.askMe(foo);

But it's not possible to mix message passing style and immutable objects.

Not the answer you're looking for? Browse other questions tagged or ask your own question.