> On May 20, 2016, at 11:17 AM, Austin Zheng via swift-evolution 
> <[email protected]> wrote:
> 
> I almost want to propose forbidding methods in protocol extensions unless 
> they're also a requirement in the protocol itself, but I don't think that 
> would fly.

That would prevent us from adding extension methods to protocols declared in 
modules we depend on.  This is way to useful to throw away.

> 
> Austin
> 
>> On May 20, 2016, at 5:56 AM, Fabian Ehrentraud via swift-evolution 
>> <[email protected] <mailto:[email protected]>> wrote:
>> 
>> Hi,
>> 
>> there's been a little discussion about static vs. dynamic dispatch on this 
>> mailing list, and there is a good post about the pitfalls when using 
>> attributes defined in extensions [1].
>> 
>> Having run into this myself during development, is there a plan on how to 
>> reduce the pitfalls in future versions of Swift?
>> 
>> - Fabian
>> 
>> 
>> [1] 
>> https://developer.ibm.com/swift/2016/01/27/seven-swift-snares-how-to-avoid-them/
>>  
>> <https://developer.ibm.com/swift/2016/01/27/seven-swift-snares-how-to-avoid-them/>
>> 
>>> Sorry, I understand and appreciate your pragmatism. Right now it feels very 
>>> much like a fight to the ideological death between POP and OOP and it may 
>>> get really bad results this way.
>>> 
>>> Sent from my iPhone
>>> 
>>> On 4 Mar 2016, at 08:58, Brent Royal-Gordon <brent at architechies.com 
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>> wrote:
>>> 
>>> >> Brent, why is dynamic dispatching for protocol extension default 
>>> >> implementations wrong in your mind? Wouldn't you agree that when static 
>>> >> dispatching introduces such a side effect that it should not be 
>>> >> automatically applied and perhaps a keyword should be added if you 
>>> >> really wanted static dispatching nonetheless?
>>> >> 
>>> >> I think that code execution should not be affected by type casting, it 
>>> >> feels like a very confusing part of the language.
>>> > 
>>> > I don't think dynamic dispatch is wrong; I think it's a large and 
>>> > technically challenging change. So in the spirit of incrementalism, I was 
>>> > trying to make cautious proposals which kept existing semantics intact 
>>> > but made them clearer, in preparation for perhaps eventually introducing 
>>> > dynamic dispatch. (Basically, I suggested that non-overridable protocol 
>>> > extension members should be marked `final` and it should be illegal to 
>>> > shadow them.)
>>> > 
>>> > But the feedback I got indicated that most people wanted a more 
>>> > aggressive proposal which introduced dynamic dispatch immediately. That's 
>>> > much harder to propose because it touches on all sorts of runtime 
>>> > implementation details I know nothing about, so I didn't try to draft a 
>>> > proposal.
>>> > 
>>> > (You are, perhaps inadvertently, currently demonstrating exactly what 
>>> > happened in those previous threads!)
>>> > 
>>> > -- 
>>> > Brent Royal-Gordon
>>> > Architechies
>>> > 
>> 
>>> > On Dec 11, 2015, at 8:56 PM, Kevin Ballard via swift-evolution 
>>> > <swift-evolution at swift.org 
>>> > <https://lists.swift.org/mailman/listinfo/swift-evolution>> wrote:
>>> > 
>>> > You think that Swift prefers virtual dispatch. I think it prefers static.
>>> > 
>>> > I think what's really going on here is that _in most cases_ there's no 
>>> > observable difference between static dispatch and virtual dispatch. If 
>>> > you think of Swift as an OOP language with a powerful value-typed system 
>>> > added on, then you'll probably think Swift prefers virtual dispatch. If 
>>> > you think of Swift as a value-typed language with an OOP layer added, 
>>> > then you'll probably think Swift prefers static dispatch. In reality, 
>>> > Swift is a hybrid language and it uses different types of dispatch in 
>>> > different situations as appropriate.
>>> 
>>> (emphasis mine)
>>> 
>>> I know that this is a bit philosophical, but let me suggest a “next level 
>>> down” way to look at this.  Static and dynamic are *both* great after all, 
>>> and if you’re looking to type-cast languages, you need to consider them 
>>> both in light of their semantics, but also factor in their compilation 
>>> strategy and the programmer model that they all provide.  Let me give you 
>>> some examples, but keep in mind that this is a narrow view and just MHO:
>>> 
>>> 1. C: Static compilation model, static semantics.  While it does provide 
>>> indirect function pointers, C does everything possible to punish their use 
>>> (ever see the non-typedef'd prototype for signal(3/7)?), and is almost 
>>> always statically compiled.  It provides a very “static centric” 
>>> programming model.  This is great in terms of predictability - it makes it 
>>> trivial to “predict” what your code will look like at a machine level.
>>> 
>>> 2. Javascript: Completely dynamic compilation model, completely dynamic 
>>> semantics.  No one talks about statically compiling javascript, because the 
>>> result of doing so would be a really really slow executable.  Javascript 
>>> performance hinges on dynamic profile information to be able to efficiently 
>>> execute a program.  This provides a very “dynamic centric” programming 
>>> model, with no ability to understand how your code executes at a machine 
>>> level.
>>> 
>>> 3. C++: C++ is a step up from C in terms of introducing dynamism into the 
>>> model with virtual functions.   Sadly, C++ also provides a hostile model 
>>> for static optimizability - the existence of placement new prevents a lot 
>>> of interesting devirtualization opportunities, and generally makes the 
>>> compiler’s life difficult.  OTOH, like C, C++ provides a very predictable 
>>> model: C++ programmers assume that C constructs are static, but virtual 
>>> methods will be dynamically dispatched.  This is correct because (except 
>>> for narrow cases) the compiler has to use dynamic dispatch for C++ virtual 
>>> methods.   The good news here is that its dynamism is completely opt in, so 
>>> C++ preserves all of the predictability, performance, and 
>>> static-compilability of C while providing a higher level programming model. 
>>>  If virtual methods are ever actually a performance problem, a C++ 
>>> programmer has ways to deal with that, directly in their code.
>>> 
>>> 4. Java: Java makes nearly "everything" an object (no structs or other 
>>> non-primitive value types), and all methods default to being “virtual” (in 
>>> the C++ sense).  Java also introduces interfaces, which offer an added 
>>> dimension on dynamic dispatch.  To cope with this, Java assumes a JIT 
>>> compilation model, which can use dynamic behavior to de-virtualize the 
>>> (almost always) monomorphic calls into checked direct calls.  This works 
>>> out really well in practice, because JIT compilers are great at telling 
>>> when a program with apparently very dynamic semantics actually have static 
>>> semantics in practice (e.g. a dynamic call has a single receiver).  OTOH, 
>>> since the compilation model assumes a JIT, this means that purely “AOT” 
>>> static compilers (which have no profile information, no knowledge of class 
>>> loaders, etc) necessarily produce inferior code.  It also means that Java 
>>> doesn’t “scale down” well to small embedded systems that can’t support a 
>>> JIT, like a bootloader.
>>> 
>>> 5) Objective-C: Objective-C provides a hybrid model which favors 
>>> predictability due to its static compilation model (similar in some ways to 
>>> C++).  The C-like constructs provide C-like performance, and the 
>>> “messaging” constructs are never “devirtualized”, so they provide very 
>>> predictable performance characteristics.  Because it is predictable, if the 
>>> cost of a message send ever becomes an issue in practice, the programmer 
>>> has many patterns to deal with it (including "imp caching", and also 
>>> including the ability to define the problem away by rewriting code in terms 
>>> of C constructs).  The end result of this is that programmers write code 
>>> which use C-level features where performance matters and dynamicism 
>>> doesn’t, but use ObjC features where dynamicism is important or where 
>>> performance doesn’t matter.
>>> 
>>> While it would be possible to implement a JIT compiler for ObjC, I’d expect 
>>> the wins to be low, because the “hot” code which may be hinging on these 
>>> dynamic features is likely to already be optimized by hand.
>>> 
>>> 6) GoLang: From this narrow discussion and perspective, Go has a hybrid 
>>> model that has similar characteristics to Objective-C 2013 (which 
>>> introduced modules, but didn’t yet have generics).  It assumes static 
>>> compilation and provides a very predictable hybrid programming model.  Its 
>>> func’s are statically dispatched, but its interfaces are dynamically 
>>> dispatched.  It doesn’t provide guaranteed dynamic dispatch (or “classes") 
>>> like ObjC, but it provides even more dynamic feautres in other areas (e.g. 
>>> it requires a cycle-collecting garbage collector).  Its "interface{}” type 
>>> is pretty equivalent to “id” (e.g. all uses of it are dynamically 
>>> dispatched or must be downcasted), and it encourages use of it in the same 
>>> places that Objective-C does.  Go introduces checked downcasts, which 
>>> introduce some run-time overhead, but also provide safety compared to 
>>> Objective-C. Go thankfully introduces a replacement for the imperative 
>>> constructs in C, which defines away a bunch of C problems that Objective-C 
>>> inherited, and it certainly is prettier!
>>> 
>>> … I can go on about other languages, but I have probably already gotten 
>>> myself into enough trouble. :-)
>>> 
>>> 
>>> With this as context, lets talk about Swift:
>>> 
>>> Swift is another case of a hybrid model: its semantics provide 
>>> predictability between obviously static (structs, enums, and global funcs) 
>>> and obviously dynamic (classes, protocols, and closures) constructs.  A 
>>> focus of Swift (like Java and Javascript) 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..  For example, the Swift 
>>> compiler can tell if methods in non-public classes are never overridden 
>>> (and non-public is the default, for a lot of good reasons) - thus treating 
>>> them as final.  This allows eliminating much of the overhead of dynamic 
>>> dispatch without requiring a JIT.  Consider an “app”: because it never 
>>> needs to have non-public classes, this is incredibly powerful - the design 
>>> of the swift package manager extends this even further (in principle, not 
>>> done yet) to external libraries. Further, Swift’s generics provide an a 
>>> static performance model similar to C++ templates in release builds (though 
>>> I agree we need to do more to really follow through on this) -- while Swift 
>>> existentials (values of protocol type) provide a balance by giving a highly 
>>> dynamic model.
>>> 
>>> The upshot of this is that Swift isn’t squarely in either of the static or 
>>> dynamic camps: it aims to provide a very predictable performance model 
>>> (someone writing a bootloader or firmware can stick to using Swift structs 
>>> and have a simple guarantee of no dynamic overhead or runtime dependence) 
>>> while also providing an expressive and clean high level programming model - 
>>> simplifying learning and the common case where programmers don’t care to 
>>> count cycles.  If anything, 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.
>>> 
>>> Finally, while it is possible that a JIT compiler might be interesting 
>>> someday in the Swift space, if we do things right, it will never be “worth 
>>> it” because programmers will have enough ability to reason about 
>>> performance at their fingertips.  This means that there should be no Java 
>>> or Javascript-magnitude "performance delta" sitting on the table waiting 
>>> for a JIT to scoop up.  We’ll see how it works out long term, but I think 
>>> we’re doing pretty well so far.
>>> 
>>> TL;DR: What I’m really getting at is that the old static vs dynamic trope 
>>> is at the very least only half of the story.  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.
>>> 
>>> -Chris
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected] <mailto:[email protected]>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to