High Leverage? Why Yes, It Is

I saw this in a recent blog post:

With high-level languages and good libraries, small teams can create great products at a rapid pace. We realized that we could write applications for the desktop in the exact same way. We rewrote SftpDrive from top to bottom in Python, with a GUI in Objective-C. It’s called ExpanDrive, and it took 1/3rd the time that SftpDrive took to develop. — Magnetk Blog via Daring Fireball

Let me tell you, they’re not kidding.

Regular visitors to the blog can’t help but have noticed the prevalence of posts on Python, Cocoa, and using Python and Cocoa together. This is because, as you might guess, Wooji Juice is also planning on releasing a commercial application for Mac OS X, making use of Python, Objective-C and the Cocoa APIs. We’re gearing up for launch soon, but I’m taking a moment out from working with the beta testers, and wrangling biz stuff, to explain why we’re going this route.

Partly, it’s because Python, as a language, rocks. Partly, it’s because, between the Cocoa APIs and Python’s own libraries, so much of what you need is already available. But there’s another factor to take into account: the quality of the bridge between them.

See, as a happy Pythonista, I’ve often wanted to write GUI applications in Python. Unfortunately, if you want to write for Windows, or for a cross-platform environment, your options are somewhat limited. Originally, this paragraph went through the list of different systems I’d tried, from tkinter to Jython+SWT, and why I didn’t want to use them, but I realised it wasn’t actually very interesting. Trust me, it’s hard to find a Python GUI system that both gives you a high-quality user experience, and isn’t deeply painful to work with.

If you’re prepared to target only the Mac platform, Python+Cocoa solves those problems, because you get a direct connection to the Mac’s native APIs. The native APIs (and your own good taste and judgement, hopefully) provide the high-quality user experience, and the direct connection is what makes it easy to work with.

Impedance Mismatch

See, what makes most Python GUI libraries awkward — indeed, most GUI libraries for high-level languages — is the impedance mismatch problem. This is an electrical engineering term that’s been commandeered by software engineers to refer to situations where two systems need to exchange data, but their ways of representing that data are very different. You spend most of your time writing code that reads data from one format, converts it to the other, and shoves it into a function call that crosses the blood-brain barrier (to mix metaphors yet again), and then probably has to do the same thing in reverse when the call exits. It can be particularly painful if that data is something like a pointer or bound method or some other concept that simply doesn’t exist in the other language.

If you were targeting win32 directly from Python, for instance, you’d probably spend a lot of time calling struct.pack() and struct.unpack(). The cTypes library helps a lot, but still, it only goes so far. You’d probably also be sticking an object’s id() into an LPARAM on a distressingly regular basis and using a dict to handle refcounting and lookup later, and worrying about when its safe to remove it again, and so on. Yuck.

Chocolatey Goodness

The thing is, both Objective-C and Python are “multi-paradigm” languages, and both of them have “Smalltalk-ish” as one of those paradigms. Under the covers, Objective-C is just plain old 40-year-old statically-typed C. But it has an object-oriented runtime layer above that, which provides a method lookup syntax that is, effectively, duck typing as championed by Python and other high-level languages. Which is, frankly, the sensible thing to do.

Add to that, Cocoa’s affection for dictionaries. Many Cocoa methods, instead of taking lots of parameters, or C-style structs, take a dictionary with particular keys for each of the values they care about.

PyObjC takes advantage of all of this, along with Leopard’s new Scripting Bridge, to provide nearly seamless integration between Python and the underlying system. Python and Cocoa data types are converted back and forth automatically — and usually, its not by copying the data into a new type, but by using duck typing to provide an interface that allows Python’s data to be read by Objective-C, or vice-versa. Dictionaries, lists/arrays/tuples, strings, binary data blobs, and even NSPoint, NSSize, NSRect, get ported back and forth seamlessly.

This is quality integration, mind. Unlike some fragile scripting bridges, you can subclass to your heart’s content, for instance, you can subclass NSView in Python, to make your own custom views. Or you can subclass Python’s list, and still pass your custom class in to Cocoa methods that expect an NSArray. The leverage that comes from these thing is the true magic, because your application’s native data types can frequently be thrown at Cocoa with no work at all, even using the higher-level “glue” facilities I just mentioned in the previous article.

So yes, you do indeed get a stonking great big lever. And a place to stand.