50

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.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
Hyperboreus
  • 609
  • 1
  • 5
  • 8
  • 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. – Robert Harvey Mar 17 '14 at 19:12
  • @RobertHarvey Sure you can have immutable objects (like python strings or tuples, or custom classes designed as such) in OOP languages. Let me rephrase: Is mutability a core feature/property of OOP design? Do "OOP" and "pure immutability" together make any sense? – Hyperboreus Mar 17 '14 at 19:16
  • 6
    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. – Robert Harvey Mar 17 '14 at 19:17
  • @RobertHarvey True, you choose whether you want mutability or immutability for each situtation. But then again, if you took away the possibility of having mutable objects, what would be left of OOP? (I am really having problems expressing my question.) – Hyperboreus Mar 17 '14 at 19:20
  • 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. – Robert Harvey Mar 17 '14 at 19:27
  • @RobertHarvey Yet mutability is a core feature of **all** object oriented languages I, and presumably OP, are aware of. The question, as I understand it, *is* whether mutability is a "core part of object-orientation" or not. What you're saying is a potential answer to the question, not an obstruction to it. –  Mar 17 '14 at 19:27
  • ***BAD*** things happen in Java if you try to imagine what a mutable `Integer` would imply. Related: [Why is String immutable in Java?](http://programmers.stackexchange.com/questions/195099/why-is-string-immutable-in-java) –  Mar 17 '14 at 19:37
  • 2
    @MichaelT The question is not about making specific things mutable, it's about making all things immutable. –  Mar 17 '14 at 19:41
  • @delnan "Basically, are (complete) immutability and OOP opposites or orthogonal?" - my comment addresses that and links to another question where the benefits of immutable structures in Java are addressed. –  Mar 17 '14 at 19:43
  • @MichaelT Feel free to point out anything I missed, but you only seem to make a case for immutability being sometimes beneficial, not for "complete" immutability, and not whether *mutability* is sometimes beneficial or even necessary. –  Mar 17 '14 at 19:44
  • @delnan I am really intrigued about your point of view about complete immutability in an OO context. – Hyperboreus Mar 17 '14 at 19:45
  • @Hyperboreus I lean towards Robert Harvey's answer, but would like to actually try using such a language (or at least think long and hard about how it would work) before making a judgement call. –  Mar 17 '14 at 19:51
  • There is only one object oriented language, and it is heavily skewed towards immutability. :-) http://stackoverflow.com/questions/3431509/is-erlang-object-oriented – Eric Lippert Mar 17 '14 at 23:23
  • 2
    http://wcook.blogspot.com/2012/07/proposal-for-simplified-modern.html – Thiago Silva Mar 18 '14 at 00:41
  • 2
    One possible implementation of an OOP system without any mutation: http://okmij.org/ftp/Scheme/#pure-oo – Alex Celeste Mar 18 '14 at 01:04
  • Bartosz Milewski wrote some relevant posts on persistent data structures, starting with: http://bartoszmilewski.com/2013/11/13/functional-data-structures-in-c-lists/ – boycy Mar 18 '14 at 09:35
  • @MichaelT: The "right" pattern is often to have related mutable and immutable classes, as was done with `String` and `StringBuilder`. Instances of a mutable class that wrapped an `int` would not be shareable in the way `Integer` instances are, but would be useful in its their own right. – supercat Mar 18 '14 at 13:54
  • You may find this article helpful, about this very subject: [Objects Should Be Immutable](http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html) – yegor256 Jul 22 '15 at 01:41

7 Answers7

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.

amon
  • 132,749
  • 27
  • 279
  • 375
  • 2
    Thank you very much, especially for the definition of the two core features. – Hyperboreus Mar 17 '14 at 19:58
  • Dynamic Dispatch is not a core feature of OOP. Neither really is Encapsulation (as you already admitted). – OrangeDog Mar 18 '14 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 Mar 18 '14 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 Mar 18 '14 at 13:55
  • Whats the difference between encapsulation and data hiding? – CodeYogi May 09 '16 at 16:54
  • 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 May 09 '16 at 17:11
  • So, generally speaking implementation details could be data or private methods and encapsulation is to what extent we can hide those details behind client's why, in that case its more of an art than science right? – CodeYogi May 09 '16 at 17:16
29

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.

Karl Bielefeldt
  • 146,727
  • 38
  • 279
  • 479
  • Well yes, but the question is exactly about those patterns you mention in the last paragraph. Give examples maybe? – Flavius Dec 06 '19 at 05: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 Sep 04 '22 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.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
  • Thank you. Could you please elaborate a bit your last paragraph ("obvious" might be a bit subjective). – Hyperboreus Mar 17 '14 at 19:37
  • Already did.... – Robert Harvey Mar 17 '14 at 19:37
  • Thank you again and good answer. I have to digest it a bit. I also think that polymorphism and (not inheritance, but) derivation also figure quite centrally in functional languages. – Hyperboreus Mar 17 '14 at 19:40
  • It's hard to see how the kind of polymorphism that we use in OO has much to do with FP. – Robert Harvey Mar 17 '14 at 19:51
  • Then maybe the term polymorphism is used differently in both contexts. I was thinking about something like `map :: (a -> b) -> [a] -> [b]`. http://www.haskell.org/haskellwiki/Polymorphism – Hyperboreus Mar 17 '14 at 19:56
  • 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 Mar 17 '14 at 20:11
  • @RobertHarvey Functions like `map` are just an abstraction for loops. Any other reason to keep mutable variables? – cimmanon Mar 18 '14 at 15:35
  • @cimmanon: You're saying that like you think mutable variables are a bad thing. – Robert Harvey Mar 18 '14 at 15:38
  • 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 Mar 18 '14 at 16:16
  • @cimmanon: The whole point of my answer is that immutability is a language construct. You're running Haskell on a machine that is mutable through and through; it is only *simulating* immutability for your personal benefit. – Robert Harvey Mar 18 '14 at 16:20
  • 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. – Hyperboreus Mar 18 '14 at 16:55
5

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.

supercat
  • 8,335
  • 22
  • 28
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.

Revious
  • 157
  • 11
  • 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. –  Mar 17 '14 at 19:40
  • @delnan I also think that the last paragraph of the answer is more about an implementational detail than about (im)mutability. – Hyperboreus Mar 17 '14 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 Mar 17 '14 at 19:42
  • 1
    @Revious By no means. Leave it, so it causes discussion and more interesting opinions and points of view. – Hyperboreus Mar 17 '14 at 19:44
  • @Revious Any persistence "sequence" data structure works, really. For example, [Finger Trees](http://www.haskell.org/ghc/docs/7.6.2/html/libraries/containers-0.5.0.0/Data-Sequence.html) of `char`s would support efficient (in the amortized time complexity sense) concatenation, splitting, removal at any index, insertion at any index, etc. –  Mar 17 '14 at 19:49
  • @delnan: but, correct me if I'm wrong, then you would have to face with time of research, lists overhead, memory fragmentation and so on. You would make modifies faster but reader slower. What would happen to a string after 1000 modifies? – Revious Mar 17 '14 at 19:57
  • 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) –  Mar 17 '14 at 21:19
  • @delnan: "Reading" a string stored as something other than a linear array might be slower, but what fraction of the time is a string read for a purpose other than making another one or performing I/O (which is apt to be slow enough to make the speed of going character-to-character irrelevant). – supercat Mar 20 '14 at 02:02
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.

cHao
  • 1,030
  • 7
  • 11
  • 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. – Hyperboreus Mar 17 '14 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 Mar 17 '14 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. – Michael Shaw Mar 18 '14 at 01: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 Mar 18 '14 at 02:00
  • And yet, [in the comments on the question we find ...](http://programmers.stackexchange.com/questions/232711/complete-immutability-and-object-oriented-programming#comment464445_232711) – dmckee --- ex-moderator kitten Mar 18 '14 at 03:23
  • Hackery :) And a lack of side effects that i can see. The side effects would come in when the caller replaces its own property with that new object. Which means "updating" itself as well, in such a way that its owner and/or associates can see. Which means having them alert their associates. Etc. In order to make complete immutability feasible, you'd need a (tail-recursive) procedure, or family of procedures, doing nearly all of the work of manipulating a bunch of disconnected objects. Which is broken all to hell, considering the reasons OOP exists. – cHao Mar 18 '14 at 07:18
  • @cHao Thank you for this comment. Your last sentence summarizes what I meant with: This is what I meant with "But then again, if you took away the possibility of having mutable objects, what would be left of OOP?" Although not all objects must be disconnected, but there cannot be circular references. – Hyperboreus Mar 18 '14 at 16:53
  • @Hyperboreus: As i think about that part, it's possible, and far less clunky than i was first picturing. An "object" would be simply a handle (name/id/whatever), the universe would contain handle->value mappings, and methods would of course take the current universe as a parameter, look up their current value, calculate something, and return the (possibly modified) universe. The handles wouldn't need to change...only the mapping to a value. So you could have nearly arbitrary inter-object relations, even circular references. – cHao Mar 19 '14 at 16:44
  • @Hyperboreus: But you still have to transact with the machine sometime. And the machine *lives* on mutation -- you only have one (or maybe a couple), so you inherently have to modify it in place. Every single useful thing it does involves reading and/or overwriting bits in ways that can never be totally reversed, at least not til we invent time travel. The best you can do is build a runtime system that walls off the dirty mutable bits...but that part of it couldn't be written in the language it's running. – cHao Mar 19 '14 at 17:20
-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.

presley
  • 11