[swift-evolution] Proposal: Universal dynamic dispatch for method calls

Chris Lattner clattner at apple.com
Sun Dec 13 16:28:38 CST 2015

random note: my previous email was very high level, so I’ve made an effort to make this more concrete and include examples, to avoid confusion.

On Dec 12, 2015, at 10:04 AM, Paul Cantrell <cantrell at pobox.com> wrote:
>> Swift isn’t squarely in either of the static or dynamic camps: it aims to provide a very predictable performance model … while also providing an expressive and clean high level programming model. A focus of Swift … is to provide an apparently simple programming model. However, Swift also intentionally "cheats" in its global design by mixing in a few tricks to make the dynamic parts of the language optimizable by a static compiler in many common cases, without requiring profiling or other dynamic information.
>> I’d say that Swift is an “opportunistic” language, in that it provides a very dynamic “default" programming model, where you don’t have to think about the fact that a static compiler is able to transparently provide great performance - without needing the overhead of a JIT.
>> You really need to include the compilation model and thus the resultant programmer model into the story, and the programmer model is what really matters, IMHO.
> First, two clarification requests for Chris on two things I imagine might lead to confusion on this thread:
> When you say “programmer model,” I understand you to mean "how a Swift programmer thinks about the language’s semantics while writing Swift code, without regard to how they’re implemented in the compiler.”

Yes.  Except in extreme cases, the interesting question isn’t whether it is “possible" to do thing X in language Foo, it is to ask whether Foo “encourages" X and how it rewards it.  For example, people can (and do!) implement v-table dynamic dispatch systems in C to manually build OO models, but C requires tons of boilerplate to do that, and rewards those efforts with lack of type checking, no optimization of those dispatch mechanisms, and a typically unpleasant debugger experience. 

What I really care about is “what kind of code is written by a FooLang programmer in practice”, which is what I refer to as the programmer model encouraged by FooLang.  This question requires you to integrate across large bodies of different  code and think about the sort of programmer who wrote it (e.g. “systemsy" people often write different code than “scripty” people) and how FooLang’s design led to that happening.  People end up writing code a certain ways because many obvious and subtle incentives inherent in the language.  When designing a programming language from scratch or considering adding a feature to an existing one, the “big” question is what the programmer model should be and whether a particular aggregation of features will provide it.  

As a concrete example, consider “let”.  A Swift goal is to “encourage" immutability, without “punishing” mutability (other languages take a position of making mutability very painful, or don’t care about immutability).  This is why we use “let” as a keyword instead of “const” or “let mut". If it were longer than “var”, some people would just use var everywhere with the argument that consistency is better.  Warning about vars that could be lets is another important aspect of this position.

As a more general example, Swift’s goal is to provide a scalable programming model, where it is easy, friendly and familiar for people new to Swift and/or new to programming.  Its defaults are set up so that common mistakes don’t lead to bugs, and that forgetting to think about something shouldn’t paint you into a corner.  OTOH, Swift doesn’t achieve this by being “watered down” for newbies, it does this by factoring the language so that power-user features can be learned at the appropriate point on the learning curve.  “Niche”  features for power uses make sense when they enable new things things being expressed, new markets to be addressed, or new performance wins to be had.  This is key to Swift being able to scale from “low level system programming” all the way up to “scripting”, something I’m quite serious about.

If you’re interested in examples of niche power-user features, they could be things like inline assembly support, “#pragma pack” equivalents, enforced language subsets for constrained environments, or a single-ownership / borrow / move model to guarantee no ARC overhead or runtime interaction.  So long as the feature doesn’t complicate the basic model for all Swift programmers, allowing more expert users to have more power and control is a (very) good thing IMO.

> When you say “dynamic,” I take that to mean any kind of dispatch based on runtime type — whether implemented using vtables a la C++, message dispatch a la Objective-C, string-based lookup in a hash a la Javascript, or anything else that uses something’s runtime type to resolve a method call.
> Do I understand you correctly?

Yes, I’d also include checked downcasting, since that relies on runtime type as well.  It is admittedly a stretch, but I include mark and sweep GCs as well, since these need runtime type descriptors to be able to walk pointer graphs in the “mark" phase.

> On this thread, there are (I think?) two related goals at hand:
> Allow dynamic dispatch of protocol extension methods even when the method does not appear in the extended protocol.
> Provide a good mental model of the language for programmers, and prevent programmer errors caused by misunderstandings about dispatch rules (if such misunderstandings do indeed exist in the wild).
> I’ll copy and paste what Chris wrote into a “Swift philosophy” checklist for Brent’s proposal, and for any others working toward these goals. Chris, please correct me if I’m putting words in your mouth!
> Provide a programmer model that:
> is high level
> is expressive and clean
> is dynamic by default
> doesn’t require a programmer to think about the fact that a static compiler is able to transparently provide great performance
> Provide a performance model that:
> is predictable
> makes the dynamic parts of the language optimizable by a static compiler in many common cases
> does not requiring profiling or other dynamic information
> does not require JIT compilation
Yes, this is a good summary.

> How do we resolve tension between these goals? The programmer model is what really matters, but we cannot reason about it without considering its impact on the compilation model. We should give the compiler opportunities to “cheat” in its optimization whenever we can do so without undermining the programmer model.

I’d consider adding a keyword (really, a decl modifier) to make it clear what the behavior is.  This provides predictability.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151213/649bf24b/attachment.html>

More information about the swift-evolution mailing list