In case it’s not clear: with recent and future articles I’m documenting problems that Mac and iOS developers solve using the dynamic features of the Objective-C runtime.
My point isn’t to argue that Swift itself should have similar features — I think it should, but that’s not the point.
The point is that these problems will need solving in a possible future world without the Objective-C runtime. These kinds of problems should be considered as that world is being designed. The answers don’t have to be the same answers as Objective-C — but they need to be good answers, better than (for instance) writing a script to generate some code.
Leaning on the Objective-C runtime feels like a temporary solution because it only exists on the Mac and iOS. Great web frameworks like Ruby on Rails, for example, can’t be built without relying on a more dynamic language. (And to me a great promise for Swift is being able to use it everywhere.)
I think it’s telling that the “dynamic” keyword isn’t even mentioned in the main language guide. Anything related to Objective-C is off in a separate set of documentation, which includes discouraging statements such as “Requiring dynamic dispatch is rarely necessary” and “use of the performSelector APIs is discouraged”.
What I didn’t add was a giant switch statement like we did in the bad old days of classic Mac OS programming. What I didn’t add was glue code in various locations setting up targets and actions at runtime that would have to be massaged and updated whenever I changed something. I’m not checking a list of selectors and casting classes around to make the right calls.
However, an even bigger worry, at least for me, is that Apple will take Brent’s concern extremely literally, and provide static solutions for exactly the specific problems outlined (and maybe a few others they can think of). There are some early indicators that this will be the case, for example that you can use CoreData from Swift, but you couldn’t actually build it.
And that would be missing the point of dynamic languages almost completely.
While it is true that ObjC’s dynamism has all kinds of problems we want to avoid, we’re also running into lots of cases where the obvious solution is dynamism, and the other solutions aren’t obvious.
One concrete example I can offer you is NSCoding, which is being reimplemented right now in corelibs-Foundation. That API pretty much needs a way to look up classes at runtime, (there’s really no other sane way to provide the feature). You could argue that we don’t need NSCoding (in fact that was argued), but ultimately, we decided we needed NSCoding more than not. So Swift 3 adds a
_typeByNameAPI which does dynamic lookups of classes by string. It landed, it exists, the ship has sailed.
Another concrete example that may be interesting is XCTest. The way XCTest works is your unit tests are written as functions on a class, and we “discover” the tests by enumerating the functions at runtime. But oops, Swift has no way to enumerate functions at runtime, so we can’t find any of your tests. The solution to this problem is actually pretty interesting: the latest proposal is to do sourcecode analysis from the compiler to enumerate your test functions, dump that somewhere, and then generate a second program that uses the dump to call the enumerated functions.
Update (2016-05-24): Paul Kim:
Looking back on my years with Java, I find the biggest problems with the platform was this idea that everything can be done by the compiler. It sounds great on paper but when implemented, it resulted in all sorts of contortions to work around not letting any dynamism in. It resulted in a lot of glue being written that I wouldn’t have to write in a more dynamic platform. It resulted in cascading changes across code to make trivial modifications just to appease the compiler. Maybe in a future post I’ll go into specific language features that I found problematic and show parallel trends/features in Swift but let’s just say that less is more. It’s an aesthetic that I always felt that put Apple above the rest and something which I feel like in some ways is being abandoned.
Ultimately, though, I need something that solves my problems. My problems aren’t performance. They aren’t type-safety (maybe it’s just me, but I rarely have issues with it and when I do, the time it takes to fix it is far less than the time specifying everything to a tee everywhere else). They aren’t being able to write clever VDLs. For me, it’s writing apps that solve my users’ problems and getting them out in a timely fashion.
Now maybe these same problems can be solved in a static way but what I’m not seeing from the static-camp are (decent) solutions. What I’m seeing are either hand-waving or the same crufty code-generation, write tons of repetitive boilerplate type of solutions that I had hoped we had left behind in the 90s.
Apple is in all likelihood the single largest owner of valuable, time-tested, customer-pleasing Objective-C code. Thus they face, as a company, the same challenges that Paul Kim and many other developers do: they can’t afford to put everything on the line, divert all their attention from Objective C, just to embrace a language that is not completely mature, and which doesn’t offer the same features as its predecessor.
Swift is a fascinating, beautiful language. How will it evolve to prove superior, in the long-run to Objective-C? By providing a suite of impedance-matched frameworks that fulfill all the needs of current iOS and Mac developers. How will those frameworks come to be in an environment where Apple’s most experienced framework developers, and Apple’s most experienced 3rd-party developers are steeped in a tradition that favors patterns not supported by Swift? I’ll be very eager to see how that plays out.
In other words — if you think that the various people writing about Swift and dynamism are anti-Swift, you’ve got it wrong.
I can’t prove that dynamic nature of Objective-C and the frameworks has been essential to the success of the Mac and iOS for app development. But a lot of people who’ve spent years — or decades — creating those apps sure think so.
Update (2016-05-25): Marco Scheurer:
Paradox: programmers advocating static typing trust that everything can be known in advance and that programmers cannot be trusted.
Update (2016-05-26): Arik Devens:
I’ve been thinking about it for a while now, and the only conclusion that I’ve come to is that it’s just far too soon to judge. Swift is practically brand new, and with such different strengths and weaknesses than Objective-C. Swift is so dissimilar to Objective-C, that I think if Apple could have gotten away with not supporting interoperability at all, they would have been much happier. They’ve managed to do an impressive thing, getting the two to play semi-nice together, but it’s an awkward fit at best.
I saw a thing on Twitter that said I’m just an old guy complaining about the new ways. Then the tweet was deleted, to the credit of its author. But let me take up the point.
It’s not the new ways that bother me — it’s the old old ways. That is, how I wrote apps before I started using AppKit. […] We remember how these problems were solved by the static languages of the day, and we don’t want to go back.
As long as AppKit and UIKit will be around, we’ll have Objective-C and its runtime. If Apple releases a Swift-only successor to either one of these frameworks, I’m confident that they will do a good job: they’ll make us write less boilerplate, rather than more.
Although on Apple’s platforms we have the Objective-C runtime, Swift on the server can’t lean on that. So there are already consequences today, not just for hypothetical future frameworks. Also, tellingly, none of the people saying that dynamic features aren’t needed have demonstrated good alternatives in Swift. Instead I see hand-waving.
I like that this doesn’t claim Brent’s anti-Swift. But I’m uncomfortable with “I trust Apple” — no-one is perfect.
People far more qualified than I am are leading this discussion. But I remember Apple heavily pushing Java over Obj-C for a while.
Best thing about Swift is forcing us to elucidate our points (again)
Worst thing is that we’re going to lose (again (until next round))
The programming language equivalent of Greenspun’s tenth rule?
When I talk about dynamic programming on iOS and Mac, I mean this:
An app can learn about its structure at runtime.
An app can do things based on what it knows about its structure.
An app can make changes to its structure.
My experience working with type strict frameworks is that they are fussy. It’s like if the only way you could chop things when cooking is with an array of specialty chopping tools that have to be assembled from pieces every time you needed them. And you have one for garlic, another for potatoes, and so on.
Every time you start to cook, you’ll feel the weight of taking those tools out, assembling the pieces, then taking them back apart, washing them, and putting them away. What you’ll find over time is that the recipes themselves will adapt to require fewer chopping tools and will become more plain and more similar over time.
So will the framework change the app. You’ll begin to avoid the things the framework makes tedious.
While I think it’s great for the community to discuss what needs to be added to Swift as it grows, I’ve also seen a lot of violations of what I’ll call “Shipley’s 2nd Law” (because my ego knows no bounds). The first law, is, of course, “Less code is better code, no code is best code.”
Shipley’s 2nd Law states, “Groups that have brought you good things in the past will tend to continue to bring you good things, and groups that have brought you shit will continue to pile shit on top.”
I think this is generally true, but one should also consider that although the LLVM/Clang/ARC team has a great track record, they are not the same people who have a history of delivering good frameworks for apps. Rather, as Shipley and Marcel Weiher note, they’re the ones who have been tightening the screws on Objective-C, making some of its dynamic features harder to use. No one doubts that this team will produce a great language for writing a compiler—the question is how it will handle apps. Apple is attempting something very ambitious: a single language that can span the spectrum. They are in uncharted territory, which is why people don’t know exactly what to expect.
dynamicmodifier exists only for interoperability with Objective-C, this isn’t sufficient evidence to say that Swift classes will gain dynamism similar to Objective-C classes.
But lookup tables are exactly how message passing works. The problem isn’t that they exist, it’s any solution where, as Guy reminds us, you have to manage and see boilerplate.
So what if Swift let you code classes that looked like Swift but behaved dynamically? What if Swift let the virtualness of methods bleed out into runtime, and it could gradually be bolted down as the application ran, depending on the actual types, actual objects, actual methods, actual messages passed, call site by call site? What if that metadata was allowed to remain, could be used later and maybe even be extended?
Crazy thing is you can have both static analysis and runtime dynamism at the small cost of code size and a perhaps a Mach-O tweak.
I’m talking about runtime phase-change from static-to-dynamic as needed.
Commemorate the Objective-C vs Swift Dynamism Wars by buying the official t-shirt.
Now, the ASP.NET team could have taken the Rails approach and implemented it more or less identically. The facilities exist within the language using reflection to do exactly the same thing. After all, ASP.NET MVC was inspired by Rails. They did not, though; they approached it with a static language mindset and in the spirit of the larger .NET framework.
I’m not suggesting that’s wrong. They should implement their framework in the style and accent that’s natural for them. What I want to point out is how differently a static view of the world can be.
There is not a switch statement or dispatch table anywhere in this code.
Instead of using a selector directly, messages are reified as instances of types conforming to the
There is the equivalent of a protocol for each selector, and he implements the messaging in a library, whereas Cocoa uses the language’s own messaging facilities.
The benefits of that additional work are:
- Static conformance checking for
- The opportunity to attach any data we wish to the event.
- No need to dynamically check the types of arguments handler.
The responder chain is cool, but I’m not sure if we need to replicate it in Swift. Rather, we could try to think of a way that is just as easy as the responder chain, but also simple. It should be easy to refactor code. It should be easy to understand. It should be easy to debug. In order to make a local change, you shouldn’t have to worry about the global effects. I’m not sure if we can solve all these issues, but I’m pretty sure we can solve a few of them.
Building on that suggestion by Joe Groff, if we can’t invoke selectors directly, we can introduce a new
Commandtype to abstract that out. The
Commandtype gives us a uniform interface to perform a command on a corresponding
Responderobject. (This interface is conceptually similar to what Matthew Johnson came up with, but is simpler and therefore hopefully easier to wrap our heads around.)
With this interface, whenever we introduce a new command, we have to define two new types[…]
Update (2016-06-07): See also: this Ars Technica discussion.
Update (2016-07-07): psu:
I find it confusing that anyone would object to more compile-time checking. I only have anecdotal evidence to offer, but in my lifetime working on the very large piece of Objective-C code that I work on I have fixed or diagnosed dozens (maybe hundreds?) of bugs that required me to go and figure something out at runtime that a modern compiler could have told me about at compile time.
Compile-time checking and static analysis are great. The problem is when said checking limits what your program can do because it’s not possible to prove to the compiler that it’s correct, even when it is. Secondly, my own experience is that (my) bugs are almost never caused by things that the compiler hypothetically could have caught. So I go through a lot of ceremony to feel better about my code without much practical benefit, at least for reliability.
The general perception is that the dynamic features of Smalltalk, LISP, and Objective-C made the transparency of these tools possible. There was no need for generated code, no lookup tables, and so on. It was all in the “native language”. A desire for this sort of runtime meta-recursiveness is a dominant trait in software developers.
This seems like a fair summary.
But these are all small points. The larger point is that you don’t want to do any of these things at all if you can help it. They are bad.
Yes, you shouldn’t if you can help it. My point is that it’s better to let programmers use them with care than to forbid them entirely. Nasty code that works in the real world is preferable to code that the compiler assures you is safe but that doesn’t get the job done because of bugs or limitations outside the compiler’s knowledge.
Stay up-to-date by subscribing to the Comments RSS Feed for this post.