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?
__get
for read-only access, but there's some overhead in that.clone
is how my methods are implemented right now. I'm implementing PSR-7 which is how this came up, but I'm not convinced this immutability is a good idea.