5

I've read and heard a lot of good things about immutability, so I decided to try it out in one of my hobby projects. I declared all of my fields as readonly, and made all methods that would usually mutate an object to return a new, modified version.

It worked great until I ran into a situation where a method should, by external protocol, return a certain information about an object without modifying it, but at the same time could be optimized by modifying the internal structure. In particular, this happens with tree path compression in a union find algorithm.

When the user calls int find(int n), object appears unmodified to the outsider. It represents the same entity conceptually, but it's internal fields are mutated to optimize the running time.

How can I implement this in an immutable way?

Max Yankov
  • 12,551
  • 12
  • 67
  • 135
  • 2
    I would say that if you do the mutation in a thread-safe manner, and it's undetectable by an outsider, then it can be considered to be immutable. – Joe Feb 06 '14 at 09:02
  • @Joe but that way I have to maintain thread-safety myself. The beauty of immutability, as I understand it, is the fact that I can leave it to the language just by specifying readonly keyword. – Max Yankov Feb 06 '14 at 09:06
  • but you've said you want to mutate it. If you don't do this in a thread-safe way, you will lose one of the benefits of an immutable type (thread-safety). – Joe Feb 06 '14 at 09:12
  • My goal isn't to mutate it — it's just the only approach I can think of. My goal is to implement a particular cache optimization, without any mutations of existing values, if possible. – Max Yankov Feb 06 '14 at 09:21
  • 1
    I could imagine a solution where your class has a lazy-evaluated property (perhaps using `Lazy`) that implements your optimization. – Joe Feb 06 '14 at 11:17
  • 1
    There is a book by Chris Okasaki "Functional data structures", maybe it can help you (if you haven't read it already). – Pavel Davydov Feb 07 '14 at 12:22
  • @PavelDavydov thanks a lot for the recommendation, looks like a very interesting read! – Max Yankov Feb 07 '14 at 19:37
  • @Joe didn't know about Lazy before (to my shame), looks like something I tend to implement a lot. Thanks! – Max Yankov Feb 07 '14 at 19:37

1 Answers1

2

Short answer: you have to ensure the thread-safety by yourself.

The readonly keyword on a field gives you the insurance that the field cannot be modified after the object containing this field has been constructed. So the only write you can have for this field is contained in the constructor (or in the field initialization), and a read through a method call cannot occur before the object is constructed, hence the thread-safety of readonly.

If you want to implement caching, you break the assumption that only one write occurs (since "caching writes" can and will occur during you reads), and thus there can be threading problems in bad cases (think you're reading lines from a file, two threads can call the find method with the same parameter but read two different lines and therefore get different results). What you want to implement is observational immutability. This related question about memoization may help you with an elegant answer.

Community
  • 1
  • 1
dureuill
  • 2,526
  • 17
  • 24