Thoughts on jQuerifying Obj-C

The other day (via Hacker News) I found a project intended to make NSDictionaries and NSArrays a lot more like jQuery in a really superficial way. It seemed like a neat idea but, upon reflection, it seemed like the person (people?) doing it didn’t really understand jQuery or Obj-C terribly well. (And I say this as a relative novice w.r.t. Obj-C.)

The most obvious difference between JavaScript code and Obj-C (once you get past all the declarations) is the dot-syntax especially in combination with () invocation of functions, i.e. (although this is now supported in Obj-C) and One of the “neat tricks” afforded jQuery by this is its chaining convention: Each function by convention returns either a reference to the thing it acted on, or a modified reference (if that’s its purpose).

So we get helper libraries for Objective-C that let us do something like MyArray.first, but we can’t do MyArray.slice because we suddenly need to use [] notation — [MyArray sliceFrom:first to:last] say. There’s nothing to stop this from chaining — just like jQuery — except for the nastiness of the syntax:

[[foo bar].baz blah];

Well, I’m not sure that would compile (I assume it would, but I never write code like this so I’d be inclined to parenthesize or use an intermediate variable), but it should work in theory.

jQuery also makes a great deal of hay from JavaScript’s functional programming tools, so you can do something like foo.each( fn ).children.each( function(this){ … } ). Again, Obj-C now has all the capabilities you need to do this, just avert your eyes from the syntax:

[[foo each:@selector(fn) target:self].children each:^((id)this){ …}];

Again, I’m just typing this stuff into WordPress rather than Xcode so it’s probably a little wonky, but again it should work in theory. (Note for non-Obj-C-heads that each:target: and each: are different methods in Obj-C.) It seems to me that this exemplifies the difference between jQuery and JavaScript on the one hand and Objective-C and Cocoa on the other. Cocoa is simply not intended for writing pithy one-liners. (Often a single method call needs to be spread out over multiple lines to be readable.) While it might be nice to have this level of compact expressivity for things like processing HTML, it’s poison for a lot of coding where you don’t want your logic to get out of control (and debugging nested / chained calls is painful — although it seems like a good debugger should be able to address this).

Using blocks more aggressively we could do this instead:

[[foo each:^((id)this){ fn(this); }].children each:^((id)this){ …}];

Now we don’t need to deal with two different cases for passing functions to our object — we just deal with closures that take a single parameter of type id. (In some respects this is cleaner than jQuery.)

Aside from the proliferation of punctuation, this is getting very close to JavaScript/jQuery (modulo the fact that we can’t freely traffic in functions as variables that are closures on the context in which they are constructed — if we have a locally sensible function foo we can’t simply pass it the same way we pass a closure.

We can define types that are specific kinds of blocks and then traffic in them as variables. And lately Obj-C allows for inline definition of NSDictionaries which makes this pretty convenient. (Of course I can’t remember the syntax.) The obvious thing to do here is define a block that takes an id (which can be anything, including an NSDictionary) as an argument and similarly returns an id. (We might be able to take this a step further and build JavaScript-like objects based on NSDictionaries, complete with prototypes and so forth, but I’m not there yet.)

So we define a general purpose block type along the lines of ^((id)subject)(id){} (I can’t remember offhand the syntax for defining a closure type, and typedefs still confuse me a bit, so excuse me if that’s hopelessly off, but I hope you get the idea — our standard closure takes an id and returns an id.)

Now we can get something like this:

[[foo each:fn].children each:^((id)this)(id){ …}];

(With the understanding that fn is a variable of the same type as ^((id)this)(id){…} — all functions in this new world take one object, presumably a dictionary, as an argument.)

Again, as alluded to earlier. The ultimate result of this approach would be to wrap a bunch of classes with NSDictionary-based replacements that behave a lot like JavaScript objects. You could wrap a timer and then mess around with the stuff it calls when it fires. You could wrap a view and throw new controls at it without thinking about it. You could create a table wrapper which is its own delegate and displays contents that get thrown into it ad-hoc. (It’s remarkably harder to stick two scrolling lists in a UIView than one — no longer!)

We can make coding in Obj-C a lot like coding in JavaScript (with jQuery) via this approach, but we are going against the idiom in Obj-C of using protocols etc. (e.g. many classes conform to protocols which allow them to have properties attached “on the fly”). But I don’t see any protocols that let me do stuff like the examples described above conveniently. You don’t need to go all the way down this path to get convenience functions that “default away” most of the parameters to a lot of common operations (e.g. creating timers, firing off background tasks, etc.) and allow one line implementations of table views.

Just a few thoughts.