Dynamic Swift
Brent Simmons (see archive for more):
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.
Previously: The Case for Message Passing in Swift, “It’s a Coup”, Dynamic Swift.
Update (2016-05-23): See also: Hacker News, particularly the comment by Drew Crawford:
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
_typeByName
API 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.
Update (2016-05-27): Samuel Ford (via Daniel Jalkut):
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.
Wil Shipley (tweet, Hacker News):
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.
Since the
dynamic
modifier 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.
#UseTheDynamismLuke (via Jonathan Wight):
Commemorate the Objective-C vs Swift Dynamism Wars by buying the official t-shirt.
Update (2016-05-29): Joe Groff shows how to dispatch a method on an Any
.
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.
Matthew Johnson shows a minimalist responder chain implemented in pure Swift:
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
Message
protocol.
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
Handler
types.- The opportunity to attach any data we wish to the event.
- No need to dynamically check the types of arguments handler.
Update (2016-06-06): Chris Eidhof (tweet):
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
Command
type to abstract that out. TheCommand
type gives us a uniform interface to perform a command on a correspondingResponder
object. (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.
17 Comments RSS · Twitter
[…] because Swift doesn’t allow tuples to conform to protocols. This is the sort of thing that annoys people about static languages. The workaround is to use a different version of […]
Seems like this is largely a solved problem within C# via the dynamic keyword.
Not that Swift is C#, but certain parallels can be drawn.
See also C# dynamic keyword reference: https://msdn.microsoft.com/en-us/library/dd264741.aspx
It seems to me that dynamism can be added to Swift, and strong types can still co-exist.
I realize that a reflection like scheme, or some other construct may need to be added to bridge dynamic to strong types (i.e. some sort of reflection). But compromise via (even rudimentary) reflection to gain dynamic typing seems like a good trade off.
In C# I know that any "." operator off of a variable declared as dynamic will be resolved at runtime, and if the member ain't there, well an exception is fired.
It could be treated no differently than trying to access something as null.
Could even have an 'if ... let' override that attempts to conform an object to a dynamic protocol. Example: Define an interface expected as a protocol, then do a 'dynamic cast' which will ensure that all members from the protocol are present on the Swift object.
There are so many ways to add dynamism that don't break the bank.
"My problems aren’t performance. They aren’t type-safety (maybe it’s just me..."
Amen!
Were I to list my top 5 pain points with writing Cocoa applications, type safety would not make the list. I really have to wonder how people are writing programs that type safety is a major issue. I don't often have type errors, and when I do, they're always trivial to fix. The runtime error message tells you exactly what's wrong! Just run your program once, and that's it.
In comparison, I frequently run into concurrency bugs -- GCD is neat but Cocoa really isn't built for concurrency (like, say, Clojure or Erlang). Worse, when I do have concurrency bugs in Cocoa, it's usually not obvious how to fix them.
Swift solves a lot of problems I never had, and ignores most of the ones I do have.
Long time ago I wrote a blog post. I think most of the points still apply.
https://medium.com/@icex33/is-swift-dynamic-enough-c54f8f4bdf1a#.7zbua6j9a
"Swift solves a lot of problems I never had, and ignores most of the ones I do have."
Very nicely said!!
As a long time Objective-C developer, I too feel that the dynamic features are critical to many powerful features of Cocoa. I would like to encourage the powers-that-be to include the equivalent of the ObjC runtime including the dynamic creation of subclasses and the creation of new IMPlementations at runtime.
I do appreciate the need and desire to maintain type safety. Perhaps the use of a (NS)MethodSignature might help satisfy this requirement by replacing
respondsToSelector(_ aSelector: Selector)
with
respondsToSelector(_ aSelector: Selector, withSignature: NSMethodSignature)
along with a construct to create the signature from a method or closure.
"Swift solves a lot of problems I never had, and ignores most of the ones I do have."
Exactly. Besides the funky syntax ("func" really??) it doesn't solve any issues for me. I don't need type safety and I don't need staticism (??). I love the dynamism of Objective-C. I want Objective-C 3.0, not a new language, and especially one that is supposed to be a jack of all trades. Swift encourages cleverness leading to obtuseness as well, which I hate.
No wireless. Less space than a nomad. Lame.
I'm very much in favour of dynamic languages, and Swift should probably handle dynamic dispatch better. However, and this is the point of the first line of this comment, "I never had those problems" is not a reason for not solving "those problems". There are many other programmers out there, with much less experience than you. Making the compiler help the programmer is not a bad idea. And maybe type safety (including nullability) is one area where the compiler actually can provide help.
Also, even if objc_msgSend is fast, it is still not possible to inline method calls without knowing what code will be called at runtime. Why is it important to be able to do that? Well, automatic inlining means that the programmer can decide if a piece of code belongs in a separate method or not (even in an extremely tight loop), without having to care about performance. IMP caching only solves half the problem. And efficiency is important, almost all relevant platforms for Swift are battery powered.
So is Swift dynamic enough? Good enough? Does it have too much type safety? Maybe, maybe not. But more importantly, very experienced programmers shouldn't say "I don't need this safety feature, so it shouldn't be in the language".
"Besides the funky syntax" ... Yes! There are some nice things in Swift besides its name. But another huge problem I have with that language is readability. I find its syntax totally obfuscated.
When I first started Objective C more than 5 years ago, one of the most beautiful design was the dynamic features and protocols. It made is really easy for one object to send another object a message, if it supported a protocol AND and implemented that message. It had features that kinda acted like python/ruby, but also static features to make sure things didn't get too dynamic.
Try doing the same thing in C# (for Xamarin) or Java (in Android) results in a lot of event handlers and listeners that need to be registered, etc.
I've got wuute a portfolio of large million plus user base apps under my belt and not once have I needed to specifically right dynamic code to accomplish a problem. Yes I've used libraries like CoreData that utilize these features heavily, bit in my experience, when I run into a code base abusing reflection or implenting the objc runtime..... It's always been bad code and becomes far more of an inherent debugging nightmare. I'm not sure who's asking for these features, but I will flat out say that if you have a stringly code base, you're likely doing it wrong. There's a reason testing is a required feature when using Ruby.
[…] it’s absolutely analogous. It’s become obvious that “unanticipated” dynamism is not something that Apple wants to support with […]
[…] number of Cocoa developers commented on the loss of dynamic behavior a while back. The conversation was very interesting and a lot of […]