How to make complex objects immutable?

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. Responses 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:

  1. Is there a better way to do this?
  2. If there isn’t, is it even worth the effort/cost to make objects immutable?
  3. If we could design a new language with immutability in mind, what would this look like?

5

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.

I will attempt to address the three questions you asked at the very bottom.

1) Is there a better way to do this?

The better way to do this is to build a generic library or language which is capable of efficiently managing the creation and updating of immutable objects. I will try to address this in the answer to the 3rd question.

2) If there isn’t, is it even worth the effort/cost to make objects immutable?

This is a hard question, but I would say that unless PHP has a robust library for this (which it may or may not, I am not familiar with PHP) purpose the answer is probably no. First, it would be inefficient and error prone if you just clone your objects. Secondly, if you are working in a language like PHP where the entire language and community is built around and used to mutability, it can be confusing for other programmers when a library is immutable.

3) If we could design a new language with immutability in mind, what would this look like?

Fortunately you wouldn’t have to design a new language to know what it would look like because it’s already been done. You could look at existing ones like Haskell or Clojure. These languages were designed with immutability in mind. Almost everything in those languages is immutable: Lists, Vectors, Maps, Sets, Records etc and they are implemented in a way which makes updates much faster than copying the entire object.

Clojure for instances uses Hash array mapped trie http://en.wikipedia.org/wiki/Hash_array_mapped_trie to implement its Vectors, Maps, Sets and Records which gives O(log(n)) insert/update speed and the underlying implementation itself is never visible to the end user (in fact small Vectors or Maps may use a completely different representation).

When almost everything in your language is immutable some of the problems you were facing go away. For instance, if the Response holds a Bag there is not such a big problem exposing the Bag because the Bag is immutable too. Also, when you create a new Response where the status code changed, but the headers were the same, you don’t need to copy the headers Bag, you can just create a pointer to it because that Bag and everything contained in it is immutable! Allowing the compiler, runtime and libraries to make assumptions about the immutability of other things in the language is part of what allows languages designed with immutable data structures to be more efficient and more usable than trying add immutability as an afterthought to a language which started off mutable.

This is actually how the popular Ring framework https://github.com/ring-clojure/ring works in Clojure. A response is just a Map:

{:status 200
  :headers {"Content-Type" "text/html"}
  :body "Hello World"}

As you can see, the response is a Map, and the headers are just another Map. What it would look like to produce a new Map with Content-Type=”text/xml” would be:

(assoc-in response [:headers "Content-Type"] "text/xml")

The assoc-in function is from the standard library which does the work from the “ridiculous” example of knowing how to get at the nested Map and rebuild the data structures it was a part of so that the programmer doesn’t need to make the explicit calls to your withHeader and withHeaders functions. There are also numerous other techniques and functions which make common operations easier. For instance if we had more than one update we wanted to make to the response we might use the -> or thread-first macro.

(-> response
   (assoc-in [:headers "Content-Type"] "text/xml")
   (assoc-in [:headers "Content-Disposition"] "attachment; filename="data.xml""))

There are also many methods in the standard libraries for these languages which could apply a function to a value in a nested structure, merge two immutable Maps together, compare Maps for equality, etc.

2

Disclaimer: I wrote this as I was coming up with it

I think ideally we would have read-only properties, like in C#. Then we could expose $response->headers without worrying it’ll be re-assigned. Then what we need is a way for HeaderBag::withHeader to return a (new) Response object, but only when we’ve accessed it via the response object — not if we’re working with it on its own. i.e. this would return a new Response object,

$newResponse = $oldResponse->headers->withHeader('Content-Type', 'text/xml')

but this would return a HeaderBag object:

$newHeaders = $standaloneHeaders->withHeader('Content-Type', 'text/xml');

This of course would be super confusing if the method returned different objects depending on how it was called, so we need fix up the syntax a bit to make it more clear…

What if we had a new “with” operator (ω) that behaves a bit like assignment (=) except it returns a new instance of the object on the left with the new property set. For example, instead of

obj.x = 5

You would write

obj2 = obj ω x = 5

Better yet, you could manipulate several properties at once while only making one copy of the object:

obj2 = obj ω x = 5, y = 6

Then going back to our example above we could write:

$newResponse = $oldResponse ω headers = $oldResponse->headers->withHeader('Content-Type', 'text/xml');

But that’s not much better. Maybe we can add another symbol to represent the object being manipulated? For example, if you wanted to increment a value, you could either write:

obj2 = obj ω x = obj.x + 1

Or

obj2 = obj ω x = τ + 1

Where τ refers to the property just after the ω. Now we can write:

$newResponse = $oldResponse ω headers = τ->withHeader('Content-Type', 'text/xml');

Getting better, but I think we can still improve it. Let’s pretend our HeaderBag implements an array interface, such that we can get a header via $response->headers['Content-Type'].

$newResponse = $oldResponse ω headers = τ ω τ['Content-Type'] = 'text/xml';

Now let’s see what it looks like to set a whole bunch of properties all at once:

$newResponse = $oldResponse 
    ω headers = τ ω τ['Content-Type'] = 'text/xml', 
                    τ['Content-Disposition'] = 'attachment; filename="data.xml"',
      status = 200,
      body = '<root>text</root>'

I think I might need some parentheses in there, or maybe I should remove the , operator and require more ωs, I’m not sure, but that’s the general idea.

We might want to replace ω with something a bit easier to type, perhaps w/? For τ I was thinking “this” but that has a different meaning inside of classes.

This needs a bit more thought, but I welcome your feedback. WuHoUnited gave some examples of manipulating nested objects in Clojure, but I’m not sure if it’s as flexible as my solution which lets you call any method at any point in the chain.


With this semi-ideal solution in mind, we can try converting it back into syntactically valid PHP:

$newResponse = $oldResponse->withProp(['headers' => function($headers) {
    return $headers->withKey(['Content-Type' => 'application/json']);
]);

Something like that. Implementation would look like:

<?php
trait WithTrait {

    public function withProp($data) {
        $new = clone $this;

        foreach($data as $prop=>$cb) {
            $new->{$prop} = $cb($this->{$prop});
        }

        return $new;
    }

    public function withKey($data) {
        $new = clone $this;

        foreach($data as $key=>$cb) {
            $new[$key] = $cb($this[$key]);
        }

        return $new;
    }
}

I think this could work, but I don’t think I want to go down this road when Mike’s solution is so much simpler.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật