The example I'm about to give is for the PHP language, but I think this scenario applies to most languages.
Let's say I have an object called Response
and I want it to be immutable. Every method should return a new instance rather than modifying the state of the current instance. Response
s have headers, so I create a method called withHeader($name, $value)
that makes a copy of my response object, sets the header, and returns the new instance. It's pretty simple to use:
$newResponse = $oldResponse->withHeader('Content-Type', 'application/json');
Then I decide the headers actually have quite a few properties and I'd rather separate this out into a separate object called a HeaderBag
. Now I want my Response
to hold a HeaderBag
but I still want it to be immutable.
I can't make the the bag a public property, like $response->headers
because even if the $headers
object is immutable, the property can still be reassigned. So maybe I add something like $response->getHeaders()
which returns my HeaderBag
object, but now how do I manipulate it?
If I move my withHeader
method into this sub-object, then I wind up with something like $response->getHeaders()->withHeader(...)
. The withHeader
method would presumably return a new HeaderBag
since it too is immutable, but then what? I guess I'd end up with something like this:
$newResponse = $oldResponse->withHeaders($oldResponse->getHeaders()->withHeader($name, $value));
Which seems a bit ridiculous; to add a single value we have to clone two objects and write this abomination of a line. Sure, we can write a helper method on the Response
object to shorten it, but the whole idea was to move all the header-related methods into the HeaderBag
class.
Questions:
- Is there a better way to do this?
- If there isn't, is it even worth the effort/cost to make objects immutable?
- If we could design a new language with immutability in mind, what would this look like?
Best Answer
An http response can naturally be thought of as immutable, but only from the moment that it has been fully constructed and issued. Before that moment, various implementations of "response" classes in various environments are generally used as builders (see Builder Pattern) which get passed around in order for various routines to add to them piecemeal all the information that constitutes the final response. So, during the lifetime of the response as a builder, it is by its very nature mutable.
Now, it is still possible to take a class which is mutable by nature and turn it into an immutable class, but what I would like to suggest to you is that although doing so might be a fabulous exercise on paper, (the entire series of Erik Lippert's articles on immutability was nothing short of fascinating,) it is not really something that you would want to be doing in practice with any non-trivial class, because you end up with abominations precisely like the one you discovered. Plus, it is clearly not worth the effort, and it is stupendously wasteful in terms of computing resources.
So, my suggestion would be that if you really like the idea of an immutable response, (I like it too,) then rename your current response to
ResponseBuilder
, let it be mutable, and when it is complete, create an immutable response out of it and discard the builder.