Ever since discovering Lisp, I’ve felt an almost… loathing for other languages. They make things too complicated. You feel that other languages encourage code that ends up ugly, verbose.. inelegant. This feeling is so well known, it even has a name, the “The Lisp Snob“. Well, I’m quite sorry, but I think it’s too late for me, I’ve succumbed to the snobbery!
It’s possible, however, to bring over some of the ideas from the Land of Lisp to other languages. Indeed this is essentially what has been happening for the past several decades, people just call it something different (“a new language”, or “new features”). Today, I’d like to show what happens when you bring one such idea to Objective-C:
It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.
PXSourceList is a wonderful and popular Cocoa view for creating iTunes-like source lists created by Alex Rozanski:
Unfortunately, the example code that created the screenshot above suffers from a very common programming paradigm called Object Oriented Programming (OOP). The OOP paradigm has dominated Earth’s developers for the past several decades. I used to follow (and teach!) it, but after learning Clojure, I’ve changed my mind. I now agree with the Alex Rozanski quote above and Rich Hickey: it is better to have a small number of core data structures (preferably immutable) that are manipulated by hundreds of functions.
For this post, I will focus on just one of the many benefits of dropping the OOP perspective: code brevity.
The chief source responsible for the screenshot above is copied below. My point here isn’t to ask you to actually read through all of it, just note its size.
Instead of this:
By using “special” dictionaries, we can greatly reduce code size, and enhance flexibility
Clojure has the concept of “records”. These are basically “special dictionaries” (AKA maps). Clojure uses maps/dictionaries all over the place, because it realizes that it’s not only not necessary to create a new class for every type of data you have, but it’s counter-productive.
Today I spent some time adapting the idea of Clojure’s records into its Objective-C equivalent simply through the creation of categories on the NSDictionary and NSMutableDictionary classes. The result is that I was able to reproduce the screenshot above with the following code (equivalent sections to the ones above):
Not only is the code above more flexible and significantly shorter, but it’s clearer as well! You can practically visualize the GUI’s structure from the code itself!
The rest of the code (related to the delegate methods) is essentially equivalent, except it’s again, shorter.
“Magic” Dictionaries with Properties!
Dictionaries are created using the _MD and _D macros (for creating mutable and immutable dictionaries, respectively). These are plain old standard Foundation dictionaries. They can be saved to the hard disk, read back, key-value coded, etc. The reason for the macros is code brevity and because they use a new dictionaryWithKeysAndObjects: method that’s added to all dictionaries through the categories (I have no idea why Apple’s engineers throught it would be better to have values come before keys as with dictionaryWithObjectsAndKeys:…).
Next, let’s have a look at how dictionary values are manipulated and obtained. Most semi-intelligent languages have a map literals and simple syntax for fetching and setting map key/value pairs. Objective-C has no such intelligence imparted to it. However, it does have the very nifty forwardInvocation: mechanism. By combining forwardInvocation: with Objective-C protocols and properties, it’s possible to create “Objective-C records”.
Instead of this:
- (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
We can now use the dot-syntax notation to do this:
- (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(return item ? : ;
Behind the scenes, item.children is turned into [item objectForKey:@"children"].
Setting values is also done through the standard dot-notation of Objective-C properties:
- (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:( )item
item.title = object;
Grab on Github
You’ll find the project for TERecord here, and the converted example of PXSourceList here.