
Dynamic Swift - mpweiher
http://mjtsai.com/blog/2016/05/21/dynamic-swift-2/
======
chris_7
Not sure I see what's going on here (it's just a blog post linking to other
blog posts?), but is this arguing that Apple should turn the safety of Swift
back into the unsafe mess of Objective-C? Horrifying stuff like swizzling,
respondsToSelector tricks, etc.? That's what Swift is trying to get away from!

Some of the classes in Apple's frameworks _need_ to be overridden, some of
them cannot be overridden, some of them don't both to call their overridden
accessors - it's a mess! You can only discover this stuff by reading the
documentation (if it's even there), when it ought to be enforced by a strict
compiler.

~~~
refulgentis
The Objective-C old guard has had a very hard time dealing with Swift. For a
couple years now, every 3 months they have another blog post hand-wringing
about what they used to do in Objective-C, how they can't in Swift, with the
implicit warning this is worrisome and _needs_ to be reimplemented in Swift,
or Swift isn't a serious contender long-term.

Most of the of the community seems to have moved on, with the understanding
that of course you can't reimplement performSelector/KVO/(insert dynamic, non-
type safe feature that usually just bites you in the ass in the long run) from
scratch, but we still have Objective-C that _already implemented those
features_ , and we will have it for years, if not decades, to come. Maybe
Apple doesn't have to open source UIKit to prove their commitment to Swift (a
short, albeit slightly unfair, summary of a blog post last week from another
member of the old guard)

There seems to be a general lack of imagination, and lack of understanding
that Apple would _never_ rewrite UIKit or other large, existing frameworks in
Swift. But, once Swift is more stable in a year or two, maybe you start
thinking about new frameworks being written in Swift, with Objective-C
compatibility, instead of vice versa.

Maybe you can't write Rails in Swift, but by god, why would you _ever_ write
Rails again? There's been two heavily up voted stories here in the past two
weeks describing how awful it is to have a career dependent on a framework so
dependent on dynamic untyped code. That matches exactly with my experience
being pressed into service writing Rails code for a couple months - the
development experience felt like the Stone Age compared to Swift, even if I
could write a method ~20% faster.

~~~
drewcrawford
So, I'm deeply involved in Swift, in the sense that: I write the odd compiler
feature.

From my vantage point it seems to be the opposite of your analysis. 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.

Now that is very interesting, and certainly less dynamic than ObjC (e.g. no
method_exchangeImplementations and other nonsense), and I would argue quite a
lot saner for the scope of the XCTest problem. But it follows easily that as
soon as we do that we could emit a program that uses that same function
enumeration to emit a giant switch statement that does dispatch-by-string. Of
course it would be opt-in, so only callers that wanted it would use it, but
there's nobody to stop you.

All of that to say, I think what is _actually_ happening inside Swift is we're
coming up with more _structured_ kinds of dynamism, rather than a knee-jerk
reaction against the ObjC philosophy. ObjC's model certainly has its issues,
and we have gotten surprisingly far without really much dynamism at all. On
the other hand, Swift constantly hits cases that are "solved" by ObjC's
dynamism, those are definitely real (doesn't get more real than the core
libraries), and dismissing them out of hand would be stupid. Meanwhile it
takes time to come up with solid "structured" approaches to all the various
cases people use dynamism in the wild, so it will be a long process of
enabling more and more kinds of dynamism over time.

As for the ObjC runtime, we will not actually have it for years. Maybe on
Apple platforms. But the decision has been made not to ship it for Linux, and
Linux is of course the next big frontier for Swift. All code that wants to run
on Linux (or run on both platforms) better not use the ObjC runtime for
anything ever. So this places increasing pressure on designers to figure out
what our answer to all the dynamic usecases will be, because there is no
seatbelt to save you on one of our platforms.

Basically, I would very much not be dismissing these critiques out of hand.
They are not (other than a few) arguing for a return to ObjC. But they are
raising issues not seriously solved by Swift at present, and we need to be
uncovering those and putting in the design effort to generate solutions for
them.

~~~
refulgentis
There's definitely some nuance to this situation – I didn't mean to imply
dynamic features aren't needed, or have no place. While writing my comment,
there was a nagging voice in my head saying "What about Swift Foundation?"

What I meant to communicate was that there's a histrionical air to most of the
commentary that is unfounded. We don't _need_ to be able to reimplement Rails,
or KVO, or Core Data – we need to come up with new ways to solve those
problems with an approach like you're seeing working in practice, a more
structured dynamism. And for what it's worth, all of those problems deserve
another try at an answer. They're some of the most tricky parts of the
platform.

------
supster
Relevant reply by Chris Lattner on the Swift Evolution mailing list:
[https://lists.swift.org/pipermail/swift-evolution/Week-of-
Mo...](https://lists.swift.org/pipermail/swift-evolution/Week-of-
Mon-20151207/001948.html)

~~~
mpweiher
Not sure how it's relevant: Chris talks about JITs and the performance
characteristic of dynamic vs. static dispatch.

What these posts talk about is the _expressiveness_ of a metasystem, and they
are generally agnostic about whether this metasystem is static or dynamic,
just that it should not be less expressive than the current dynamic one.

------
kylnew
I think ObjC has left us with some dynamic habits we don't actually always
need, particularly in all non-UI layers. I appreciate the way using Swift has
me questioning just how much dynamism and mutability my programs really need.
Overuse of things like setValue:forKey: and performSelector: have a bit of
code smell anyway and there's no reason you can't replicate these kinds of
behaviors, if you really need to, with Swift Dictionaries.

~~~
Tloewald
It seems that (a) we need Dynamic features (introspection, dynamic dispatch)
and (b) we need to use it sparingly. In short, it's a _cultural_ problem, not
a technical one.

I suspect the solution is something as simple as a
"DangerouslyCallMethodByName" method.

~~~
mahyarm
you could do

    
    
      if let method = classInstance.methods[methodKey] {
        //Put some ugly method argument checker thing here
        method.execute()
      }
    

It would be typesafe, not dangerous, and statically typed.

------
adamnemecek
"Great web frameworks like Ruby on Rails, for example, can’t be built without
relying on a more dynamic language."

You don't necessarily want dynamism, you want metaprogramming.

~~~
supster
Interesting, could you provide some examples?

~~~
alehander42
A lot of Rails/Ruby magic hides in the dynamic "generation" of code and in the
nice DSL-s. However you can typically achieve the same using e.g. macros
(which work on compile time) and which can be even statically typed.

The DSL thing is orthogonal: it's more related to the language syntax than to
the type system.

------
batgar
What's stopping Swift from using the C# dynamic keyword scheme as a starting
point?

[https://msdn.microsoft.com/en-
us/library/dd264741.aspx](https://msdn.microsoft.com/en-
us/library/dd264741.aspx)

C# does an excellent job of bridging dynamism and hard types.

The use of the dynamic keyword ensures that you know you are bridging to an
area where the "." is not type checked, and is strictly evaluated at runtime.

There may be too many hooks into the reflection area of C# / .NET, however
that can be mitigated in other ways within the confines of Swift.

Could even have an 'if ... let' Swift scheme that attempts to conform an
object to a specific dynamic protocol. Example: Define an expected interface
as a protocol, then do a 'dynamic if..let cast' which will ensure that all
members from the protocol are present on the Swift object.

------
DHowett
> Leaning on the Objective-C runtime feels like a temporary solution because
> it only exists on the Mac and iOS.

That's not entirely true; libobjc2[0] exists, for example, and is fully
compatible with objc4.

[0]:
[https://github.com/gnustep/libobjc2](https://github.com/gnustep/libobjc2)

------
ebbv
I don't want to call someone I don't know a bad developer, but as a general
rule if software is moving more towards safety and reliability and you are
resisting that change that's a sign there's a problem with your habits and
preferences. Not that the decisions being made are bad.

Swift is not a perfect language, there's a lot of low hanging fruit (some of
which is being dealt with in Swift 3), but it's lack of support for dynamic
behavior is not one of those problems.

~~~
andybak
> if software is moving more towards safety and reliability and you are
> resisting that change that's a sign there's a problem with your habits and
> preferences

Unless safety and reliability come at the cost of some other metrics that you
consider more valuable?

Currently - at least in the HN bubble - it appears that the pendulum is
swinging towards static typing and away from dynamic language features. It is
surely premature to conclude this is a permanent trend.

------
krelldoggy
Unreal the name "Smalltalk" is never mentioned in this thread...a very old
argument folks.

------
comex
The posts in the first linked series
([http://inessential.com/2016/05/](http://inessential.com/2016/05/)) seem to
mostly focus on how dynamism can avoid the need for boilerplate. For example,
he states that the following code is tedious to write and error-prone:

    
    
        if localObject.foo != serverObject.foo {
          localObject.foo = serverObject.foo
          changeDictionary[fooKey] = serverObject.foo
        }
        if localObject.bar != serverObject.bar {
          localObject.foo = serverObject.bar
          changeDictionary[barKey] = serverObject.bar
        }
    

But there's another language feature that can replace boilerplate, _without_
dynamism and its associated performance and type safety penalties. A feature
that's reviled by many, and was omitted from Swift, but serves this purpose
perfectly: macros. What if you could write this?

    
    
        macro_rules! merge {
            ($localObject:expr, $serverObject:expr, ($($prop:ident),+)) => {
                $(
                    if $localObject.$prop != $serverObject.$prop {
                        $localObject.$prop = $serverObject.$prop;
                        changeDictionary[stringify!($prop)] = $serverObject.$prop;
                    }
                )+
            }
        }
        merge!(localObject, serverObject, (foo, bar, baz));
    
    

Actually, this is valid Rust code, as you might have guessed if you know that
language. I picked it because it has similar syntax to Swift and a powerful,
hygienic macro system. (This case is simple enough that a C macro would also
work fine without any ugly arcane stuff - you'd have to have a separate macro
invocation for each property, but whatever. But Rust's system scales better to
slightly more complicated scenarios.)

Admittedly, not everything you can implement with dynamism is as easy to
replace with macros. Many things are essentially impossible with the simple
pattern matching language used above. In Rust, there is also the option to
write a compiler plugin that can do arbitrary-ish things with ASTs, so almost
anything is _possible_ with enough work, including things you can't implement
with dynamism, but fiddling with AST transformations is hairy enough that
boilerplate looks a lot more attractive, relatively speaking.

Even so, I bet macros would satisfy most of the people advocating for "Dynamic
Swift". And for the people who hate them - do you really think doing crazy
things at runtime is any safer or easier to understand? It's certainly slower.

(By the way, you can implement dynamism with macros. For example, you could
autogenerate functions like 'setProperty(name: String, val: Any)' that switch
on the name and do the appropriate casting and such. Though depending on your
attitude, you might consider this crucial enough that it should be built into
the language.)

~~~
catnaroek
> It's certainly slower.

That's the least of problems, since nowadays we have smart runtime systems
that dynamically optimize running programs. It's not elegant, but it works. A
more fundamental problem is that this approach to programming leads to brittle
code, and when it breaks, it doesn't warn you until it's too late.

~~~
comex
Actually, I feel a bit silly and maybe confused, as the 'setProperty' I
mentioned at the end of my last post is... just reflection. Which Swift
already has, in the form of Mirror. The only reason the Objective-C runtime is
needed for that example is that Mirror apparently only allows you to get the
values of properties, not set them, but that just sounds like an
implementation limitation that could easily be lifted - clearly built-in
reflection is considered within the scope of Swift.[1] So it's a bit odd for
Brent Simmons to put this in a list of things he's "nervous" won't be possible
in the future, and not even mention reflection. Most of the other things in
the list seem like they really aren't possible without Objective-C or, in its
stead, new Swift features. (They could still be done with macros!)

I guess reflection fits into the general category of dynamism even if it is
essentially an existing Swift feature.

In any case, to respond to your point: Objective-C does not have a JIT, and
since there are a lot of advantages to a pure ahead-of-time compilation model
(see the mailing list post linked elsewhere in this thread), as a language
mainly focused on static compilation, Swift is unlikely to get one in the
future just to deal with hypothetical dynamic features. Anyway, even in the
relatively small set of languages that are both highly dynamic (going beyond
simple reflection) and well-optimized - there's JavaScript, and LuaJIT I
guess? and maybe some less common ones, there's PyPy but I don't call that
well-optimized - if you actually _use_ the dynamic features it's easy to fall
off the JIT happy path and make your whole function unoptimizable. See [2].
But that's getting off topic.

[1] In making that thinko I showed my lack of Swift experience: in Rust
reflection is done with macros.

[2]
[https://github.com/petkaantonov/bluebird/wiki/Optimization-k...](https://github.com/petkaantonov/bluebird/wiki/Optimization-
killers)

~~~
jonhohle
A JIT for what? Both Swift and Objective-C are AOT. Objective-C offers runtime
dispatch into native code, no different than calling a function pointer in C
(just with a more convenient method of looking them up at runtime).

------
aaronhayman
Perspective: I both develop in Swift and manage a team of 5 iOS engineers
(including me). The majority of our project now is in Swift, including over 8
dynamic modules/frameworks and 10's of thousands of lines of code (all that to
say it's not a small project).

I have to agree that there are some pain points with Swift:

\- I developed a persistence layer (not Core Data), and had to rely on a
"translation layer" that all clients must implement in order to use it. It
ends up being about 100+ lines of glue code for each model we want to persist.
Not ideal. \- Retrieving data from API services and converting the data (JSON)
into models results in a lot of glue code... A ton it. \- I've frequently had
to hack my way around the lack of Generic Protools (using base classes
instead). \- Compile times suck. Feels like I've been thrown back a decade in
this regard.

Despite these issues, not a single one of us would go back to objective-c.
Overall, the velocity of our team has only gone up since we started using
Swift:

\- Type safety and Optionals have removed an entire class of bugs in our app,
freeing up more time to write features. \- Generics have added a level of
clarity to the code base that wasn't even possible before. \- The code base is
easier to understand, easier to maintain and easier to modify. \- Oddly, the
need to write Unit Tests and use Swift has literally "forced" us into
protocol-based programming...a good thing IMO. I'm seeing much better software
architecture as devs can't lean on automatic mocks, forced instead to create
protocols and keep classes/types separated and injectable. To be clear, it's
more difficult to write that kind of code, but the quality of code has gone up
since we switched to Swift.

I've noticed that some of the complaints about the lack of dynamism in Swift
don't seem as valid. For example: Responder chains. This is easily done using
a protocol (something we did for deep linking within the app).

Other issues, like observability, can be solved by things like React, and
using Observables. Not only is React more powerful than KVO, but it's obvious
where it's being used in the code base (because you're using an Observable).

I can't deny that there are places where having dynamism would certainly
remove a lot of glue code. But my experience is that the "old guard" (me
included, really) relied too much on the dynamic nature of objective-c to
solve problems that could have been better solved in another way.

While I would like to see dynamism come to Swift, I would NOT want to see it
imported wholesale from objective-c, but instead conservatively placed and
annotated in such a way as to say to those of us who use it: "Here be
dragons...proceed at your own peril".

