11

I've become somewhat addicted to using immutable collections (mainly in Clojure, which calls them "persistent data structures"), and would love to be able program this way in some contexts on iOS and OS X.

A key example of where this would be useful is to be able to "change" a dictionary by creating a modified copy, and have change listeners be able to query the difference between the old and new values, rather than try to codify the change as a property change event. Immutable data structures are also a game-changer for concurrent programming: no need for locks.

Yes, you can do this now using the immutable NSArray and NSDictionary instances, but it becomes increasingly inefficient to copy them to make "changed" versions as you have larger and larger collections and/or make changes frequently: a small change to a large data structure then involves a disproportionate amount of work.

I'm looking for a way to enable immutable data programming in Objective-C. To clarify what this might look like, and for some more of the advantages it offers, the research by Phil Bagwell referenced in this SO question is highly relevant.

Community
  • 1
  • 1
Matthew Phillips
  • 1,275
  • 1
  • 12
  • 19
  • 3
    Define "inefficient"; as long as you stick to property lists and pay attention to compiler warnings, you can emulate much of any of the persistent patterns available. Obviously, it'll take true runtime support to do the full "faulting / copy-on-mutate" dance. I.e. the *close* votes are due to the lack of concrete examples in your question. – bbum Aug 13 '13 at 02:38
  • I've expanded the question and added some specifics. But please note that I'm not referring to "persistent" as in durable, saved-to-disk, etc, so runtime support isn't necessary. – Matthew Phillips Aug 13 '13 at 02:57
  • I've reworded the question again. I do believe this is on-topic for SO, and any answers would be useful to its users. I have not been able to find any advice on this after quite a bit of searching. – Matthew Phillips Aug 14 '13 at 01:39
  • It sounds like you might want to use object controllers. Look at NSArrayController – uchuugaka Aug 14 '13 at 03:35
  • 1
    As per bbum's comment though — define inefficient. E.g. taking a copy of a dictionary doesn't copy any of the values, taking a copy of an array doesn't copy any of its contained objects, etc. Copies are generally shallow. – Tommy Aug 14 '13 at 04:04
  • 4
    Be careful, going down this path of trying to shoehorn one method of coding into another's method can often lead to frustration, panic attacks, depression, weight gain and especially loss of hair. – Ralph Caraveo Aug 14 '13 at 04:24
  • uchuugaka: thanks, but the event notification example I gave wasn't so much about the notification aspect as about being able to see the old and the new values. NSArrayController (and it's ilk) are about emitting events about a mutable object *that's already been changed*. – Matthew Phillips Aug 14 '13 at 05:53
  • Tommy: I'm talking about nested trees of objects, i.e. nested NSArray's and NSDictionary's. The leaves can be shared, but the branches can't, so changing a single leaf value means doing a disproportionate amount of copying. This can be done (I'm doing it), but it has an impact on memory allocation overhead that doesn't necessarily need to be there. – Matthew Phillips Aug 14 '13 at 05:57
  • Ralph: I understand and totally agree. You won't find anyone lazier and more willing to go with the grain than me :) I've only come to this after trying the usual approaches. And it's really more akin to a different algorithm than new way of coding, if that makes sense. – Matthew Phillips Aug 14 '13 at 06:00
  • I'm not aware of anything emulating typical functional-language data structures in ObjC. If the mutation rate is low you can sometimes get decent sharing/thread-safety properties with a "copy mutable then freeze" approach, or copy-on-write proxy objects. – Catfish_Man Aug 14 '13 at 06:38
  • Catfish_Man: can you elaborate on what "copy mutable then freeze" involves? Both of those sound like they enable concurrency, but still need a lot of copy overhead. – Matthew Phillips Aug 14 '13 at 06:49
  • I think you need to write your own library from scratch by writing primitive functional containers such as [Finger Tree](http://en.wikipedia.org/wiki/Finger_tree) or [Zipper](http://en.wikipedia.org/wiki/Zipper_(data_structure)). Hash-table is known to be harder than sequential array. – eonil Sep 08 '13 at 10:02
  • I believe `NSArray` is partially employing these concepts under the hood, but nothing is guaranteed. – eonil Sep 08 '13 at 10:04
  • 1
    Also, if you want concurrent execution performance, consider writing low-level primitives in C/C++ with higher level Objective-C wrappers. AFAIK, Objective-C runtime performs atomic-operations to manage reference counting which may degrades concurrent execution performance somewhat. – eonil Sep 08 '13 at 10:06
  • Have you taken a look at `mutableArrayValueForKey:`, `mutableSetValueForKey:` and similar methods? I'm not entirely sure that's what you're looking for, but they do provide the functionality you're after (at least for arrays and sets). – Ian Oct 07 '13 at 03:15
  • @Ian: thanks, but those methods modify an object in-place. What I'm after are (hypothetical) methods that return a new object with the changes, and that do so efficiently. – Matthew Phillips Oct 09 '13 at 03:16
  • I see. Not sure about the impact of `copy` but I'm pretty sure it's not that inefficient. – Ian Oct 09 '13 at 12:32
  • @Ian: `copy` is probably fairly efficient, but I'd need to use `mutableCopy` which likely actually does copy all the data. – Matthew Phillips Oct 15 '13 at 05:08
  • 1
    @MatthewPhillips Actually it doesn't. Take a look at [this snippet](http://pastebin.com/c6ykuRv2). You'll notice the address for the number doesn't change. Dictionaries don't copy data, they just retain it. – Ian Oct 15 '13 at 12:22
  • @ian: thanks for pointing that out. What I meant was nested arrays and dictionaries would need to be copied. Turns out `mutableCopy` does not make mutable copies of nested arrays and dictionaries, so what I need is `CFPropertyListCreateDeepCopy`. See also my comments on tjklemz's answer below. – Matthew Phillips Oct 22 '13 at 05:21

2 Answers2

3

Please see this article at Ridiculous Fish (written, I believe, by Cory Doras, an engineer on the AppKit team and also creator of the Fish shell):

Array: Our arrays, aren't. http://ridiculousfish.com/blog/posts/array.html

You've answered your question already:

Yes, you can do this now using the immutable NSArray and NSDictionary instances...

The beauty of the Cocoa framework is its simplicity, especially concerning data structures. The idea is that the behind-the-scenes code should determine how to implement the structure, not you. In practice, you only ever need two "types" of data structures: Arrays and Dictionaries (or Maps, if you've come from other languages). Of course, you need many "types" of implementations, but you only really need two ways of accessing your data; if you need more ways, then that's where custom classes and composition come into play.

As for your concern of efficiency: don't worry about it. The article by Cory (Ridiculous Fish) reveals that under-the-hood, Apple has already met your terms for efficiency. It is after all just pointers, as Ian Murray pointed out in the comments: everything is reference counted and only copied if necessary. It is most probable that when you "copy" or "mutableCopy" an NSArray or NSDictionary that the underlying data is not actually copied. To see how this could be implemented, see Rob Pike's article on the Go language here: http://blog.golang.org/slices. I'm almost certain that Cocoa follows a similar pattern, perhaps even to a further extent.

Additionally, with the advent of Objective-C "blocks," it is now more and more feasible to program in a functional style à la LISP variants (such as Clojure). In fact, I would highly recommend this and encourage you to continue on this path. It can lead to much stabler, cleaner code if done right.

tjklemz
  • 1,170
  • 12
  • 19
  • However, I don't think I'm communicating what I'm after: the point being made in the blog you referenced is that modifying arrays in-place can be implemented in clever ways, and not to assume anything. I'm talking about _making modified copies of_ dictionaries and arrays, leaving the originals intact. To my best knowledge, the only way to do this now is to use `CFPropertyListCreateDeepCopy ()` to make a mutable copy, and then change that. Perhaps the snippet at http://pastebin.com/qgsBETSu might give you more of an idea of what I mean. – Matthew Phillips Oct 22 '13 at 05:18
2

I don't think there's a shortcut here.

Just as you imply, Clojure's persistent data structures are quite a different thing from the immutable collections classes that Cocoa provides.

If you want to use Clojure's persistent data structures from Obj-C, the only way to do so is to re-implement them in Objective-C. My understand is that many of these are described in Okasaki's book, Purely Functional Data Structures, and in the papers of Phil Bagwell.

This other answer has some links: What is the data structure behind Clojure's sets?.

Community
  • 1
  • 1
algal
  • 27,584
  • 13
  • 78
  • 80
  • I'm selecting this as the answer, since it seems that indeed I would have to port persistent data structures to ObjC. (Will be interesting to see how this relates to Swift, which seems to have some sort of copy-on-write mechanism for its dictionaries and arrays). – Matthew Phillips Jun 12 '14 at 03:03