Replacing Objective-C and Cocoa
Ash Furrow wrote an article arguing that Apple needed to replace Objective-C with something else. The crux of the argument is that programming languages have moved to higher levels of abstraction over time, edging further away from direct hardware access. By the time such a transition were completed (say within 10 years), using C-based languages will seem as archaic as using assembly. Ash then lays out features he would like to see in such a language.
Replacing something as fundamental to a platform as its language is no small feat. Apple did this once before with Cocoa and the compatibility bridge of Carbon when moving from OS 9 to OS X, and its migration took 12 years to be fully finished in public API. Developers fought this change for many years before Cocoa became the de-facto standard. So a migration to something newer cannot be a cavalier move done to embrace trends; it must be done with a clear purpose that fixes common issues in the thing it replaces, and it must set a foundation upon which to build at least a decade or two of software. And it must coexist with that which came before it. With the OS X transition, Apple didn’t just have a new language; they had a whole new operating system. It came with entirely different ways of handling memory, threading, files, and graphics. It delivered frameworks that were way more usable than their predecessors. It wasn’t just a new programming language; it was a revolution in how we built software.
That’s what it should take to inspire a radical change in developer tools – improvements on an order of magnitude in building software, making it easier to solve hard problems, and fixing issues in common coding standards that have arisen through heavy use. This goes beyond just a programming language; it will require new frameworks and design patterns to really bring about the benefit. Apple owns their developer technologies stack; from compilation with LLVM, to language features in Objective-C, to framework features in Cocoa, to web technologies in WebKit. When you have control of all of these pieces, the problems at the top of the stack can be addressed at the bottom, and vice-versa.
Here are some things I’d love to see in a next-generation developer platform.
- Concurrency. Apple added one of the biggest breakthroughs in libdispatch, a far superior method of handling concurrency and flow control than any system based on threads. A new language should add the kinds of concurrency control in libdispatch as primitives to the language itself, so that everything from the bottom up can use it. Basically if they use the word “thread” anywhere in the API, they blew it. If we can kill the concept of a god main thread as well, all the better.
- Functional processing. Functional languages like Haskell have significant benefits by keeping most data immutable and changing it with transformation functions. This has significant benefits in reducing bugs, creating less error-prone code, and most importantly reducing the kind of bottlenecks that kill concurrency like locks. We’ve got the CPU cycles for it.
- …but keep it imperative. Pretty much anybody who can code can write imperative code. Purely functional languages are not heavily used, and until they are, it’s just not realistic to expect everyone to figure this out.
- Strong types and duck invocations. One of the biggest causes of crashes I’ve seen in Cocoa has been because someone passed something they shouldn’t have to a method.
idin Objective-C can be useful, but it has been abused by developers who take shortcuts that end up biting them in the ass. Strong typing means that passing the wrong thing is an error. It’s worth the headache. Standardize on method names, so that design patterns emerge naturally. If an object responds to a
toString()function, let me call it without having an interface or protocol set up for it.
- One style to rule them all. Remember getting angry about dot-notation? Let’s avoid that. Whatever systems exist for common problem should have exactly one way to solve it, and that solution should be simple. While we’re at it, let’s take a cue from languages like Python and make syntatical style choices hard rules, so that we all (humans and IDEs) just learn them and get on with it without arguing about tabs or spaces. Then getting rid of braces and semicolons becomes much easier to do.
- Compiled. Code compiled up front is always going to be faster.
- Secure. 10 years ago, our lives lived on one PC. Now we have multiple devices tracking everything about our lives. Every machine is an attack vector for all of our highly personal data. Security should be the default. Subprocesses with strong sandboxes and privilege separation should be the default, encryption should be easy, and establishing trust will be necessary. Privacy controls for people should guide most of these decisions.
- Modular. Open source has changed how we develop apps. Instead of developers writing everything themselves only on a monolithic framework, apps now include source code written by dozens of developers building tiny, reusable components. CocoaPods is now a thing. Most modern languages have package management as a feature, because it makes it faster to prototype and build software.
- Move beyond C. No pointers. No
main(). No manual memory management. ARC was a massive improvement in how we deal with memory, but there are still issues with hard-to-debug circular references and zeroing weak references. This should be easier, more obvious, and easier to debug.
- …but let me get back to it. Decades of code have been built on C, so we should be able to get back to it. Most higher-level languages include some form of foreign function interface to get to lower-level bits of code. Just keep it in an isolated sandbox so it doesn’t crash the app.
- Interoperable. Apps should be able to talk to each other, and pass data between each other, through standardized interfaces. If the language (or at the very least the libraries) support this, we can build more powerful apps, and do a better job applying security practices.
- Introspective. Getting into the guts of a program at runtime can have some powerful benefits. We should be able to fiddle with the internals of our software. Most of the rules laid out here should be bendable. But to keep the integrity of the system in place, this API should be difficult to use.
- More sophisticated UI. In 2014, most displays showing Apple software sit between 4 inches and 13 inches. In the next ten years, that may span from 1 inch watch displays to 60 inch TVs. Concurrent graphics rendering and better support for remote screens a la AirPlay Mirroring should be APIs we can use.
- Powerful networking. If I asked you to name an app you use regularly that didn’t talk to the Internet, you’d probably have a hard time answering. Networking should be easy, powerful, secure, extensible, and designed to withstand failures.
- Easy data modeling/storage. Core Data remains a wonderful tool for development, but it was designed for an era long past, not for the present. Getting a stack running correctly has been a pain since the framework’s inception, and that’s before you try shuffling data into it from, say, a JSON-based web service. A new system built with concurrency and interaction with remote services is essential.
- Social. The Internet communications devices we all carry know all kinds of stuff about our friends and families. Apps should be able to move content between people in ways that are easier than sticking a photo in an email.
- Real-time communication. Moving data quickly between components, apps, devices, and services is super important. To create the illusion that everything works together seamlessly, this needs to be real-time with as little latency as possible. The OS can do this better than any developer can.
- Design to fail. Most apps crash because the developer did something dumb. As someone who has caused thousands of crashes on devices, I’m as guilty as anyone. Because in today’s programming environment, accounting for failure is hard. Every consideration of any new language and its resulting libraries should be designed to make it easy to notice and catch problems quickly. Crashes are not always avoidable, but if you make it simple to handle, they become much more manageable.
To summarize: a new developer platform includes a language designed for concurrency and safety, tools better tuned for writing/reading/debugging code, and powerful frameworks for building apps that can easily talk to our friends, apps, services, and devices. Such a platform would be very well suited for the current hardware we own (laptops, phones, tablets, TVs) and can expand to other device categories like smart watches, cars, appliances, and office tools. It would help us make better, safer, more secure, and more stable software.
When Apple came out with Cocoa in 2000 and improved it over the early 2000s, their pitch was that it enabled indie developers and small development shops to build software of a class previously attainable only by large companies like Adobe and Microsoft. This was a big part of what inspired me to begin teaching myself how to write Mac apps. In the years that have followed, the demands of modern Internet apps has exploded, and we are back at a stage of competitive complexity. A new developer platform could once again level the playing field and inspire a new generation of developers to aim high.