I started putting together a proposal, hopefully better explaining what I’m 
suggesting:

https://github.com/karwa/swift-evolution/blob/patch-2/0000-template.md 
<https://github.com/karwa/swift-evolution/blob/patch-2/0000-template.md>

> On 17 Sep 2016, at 05:32, Xiaodi Wu <xiaodi...@gmail.com> wrote:
> 
> On Fri, Sep 16, 2016 at 20:28 Karl <razie...@gmail.com 
> <mailto:razie...@gmail.com>> wrote:
>> On 17 Sep 2016, at 01:45, Xiaodi Wu via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
> 
>> I absolutely agree that it's a problem worth solving. However, the question 
>> is whether a particular proposed design solves the problem and avoids 
>> previously stated weaknesses. What I'm saying here is that, so far, the 
>> conversation in this thread has involved reiterating already-proposed 
>> designs that have been critiqued. It's really quite tiring for me too to 
>> repeat the same critique when someone raises the same proposal a second or 
>> third time.
>> 
>> It's possible that it makes sense to have a separate syntax for retroactive 
>> modeling. I haven't been able to come up with one that seems reasonable to 
>> me, or I would have written to this list to propose it. Do you have such a 
>> design in mind?
>> On Fri, Sep 16, 2016 at 16:59 Charles Srstka <cocoa...@charlessoft.com 
>> <mailto:cocoa...@charlessoft.com>> wrote:
>> On Sep 16, 2016, at 4:08 PM, Xiaodi Wu via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>> 
>>> We've had this discussion on the list multiple times already. The gist of 
>>> the difficulty here is that most proposals for a mandatory keyword don't 
>>> permit retroactive modeling, so it's a no-go. On the other hand, the core 
>>> team seems to take a dim view to optional syntax, since that's more in the 
>>> ballpark of linters.
>> 
>> Numerous solutions to your objection have been proposed; you always simply 
>> dismiss all of them in favor of your dogmatic stance. It’s really quite 
>> tiring. You can have this and support retroactive modeling; you just might 
>> need to have a separate syntax for retroactive conformances. You keep 
>> bringing that up as a hard-and-fast objection, but you know what? Maybe 
>> retroactive conformances should have a separate syntax, because they’re not 
>> saying the same thing! One is saying "here are some methods that will make 
>> this type conform to this protocol”, where the other is saying “this type 
>> already has the methods that conform to this protocol somewhere.” These are 
>> not the same thing, and it might be confusing to see a conformance 
>> declaration and assume it’s the former when it’s actually the latter, and 
>> then have trouble finding the conformances. Maybe it would actually make 
>> your code clearer if retroactive conformances were required to declare “this 
>> method exists somewhere else already.” Maybe you could even command-click on 
>> it and jump to the actual declaration. Anything would be better than the 
>> current situation, because:
>> 
>> The reason this keeps coming up is because it’s a real problem. I myself 
>> have started taking up the practice of always using copy-and-paste to 
>> declare conformances to protocols, because otherwise the chances of 
>> mistyping something and having the bug not manifest itself until runtime is 
>> simply too high. This is not a “linter” problem; this affects basic 
>> functionality and makes protocols, honestly, really dangerous to use. For a 
>> language that bills itself as “protocol-oriented”, it’s really quite damning 
>> that its protocol support is this brittle and fragile compared to its 
>> support for traditional inheritance. I’ve been bitten by this enough times 
>> by now to somewhat regret the decision to go with a protocol-based design. 
>> This is a real shame because conceptually, the idea of Swift’s 
>> protocol-based design is really cool.
>> 
>> Charles
>> 
> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
> 
> 
> 
> I don’t see what the big problem about retroactive modelling is.
> 
> Let me give a concrete example of how retroactively modeling is used. 
> Currently, there is a JIRA bug that Set does not conform to SetAlgebra. To 
> fix this issue, someone simply needs to write `extension Set : SetAlgebra { 
> }` and some tests. That's literally what the bug (filed by a core team 
> member) tells you to do. It's a starter bug, and if someone hasn't taken it 
> yet, you the reader could have a go at it. What's neat about Swift is that 
> it's super easy to provide the same functionality in your own project without 
> waiting on that bug to be fixed in Swift itself. You can simply write a 
> single line of code. By contrast, if your proposal were to be implemented, 
> this would become much more difficult.
> 
> This is actively used in Swift today. For example, in the Swift 
> implementation of NSScanner, you'll find the following lines:
> 
> ```
> internal protocol _BitShiftable {
>     static func >>(lhs: Self, rhs: Self) -> Self
>     static func <<(lhs: Self, rhs: Self) -> Self
> }
> 
> internal protocol _IntegerLike : Integer, _BitShiftable {
>     init(_ value: Int)
>     static var max: Self { get }
>     static var min: Self { get }
> }
> 
> extension Int : _IntegerLike { }
> extension Int32 : _IntegerLike { }
> extension Int64 : _IntegerLike { }
> extension UInt32 : _IntegerLike { }
> extension UInt64 : _IntegerLike { }
> ```
> 
> If we adopted your proposed syntax below, it would take considerably more 
> lines of boilerplate code to express the same thing. The burden increases 
> significantly with the complexity of the retroactive modeling. For instance, 
> if the retroactively modeled protocol had 20 requirements and you were 
> retroactively conforming 20 types, that'd be at least 400 lines of 
> boilerplate.
>  
> Basically, the way I see it, if my class MyClass implements MyProtocol, 
> providing someRequiredFunc(), there’s an “ownership” chain there (reading it 
> backwards).
> 
> Now what happens if MyClass implements MyOtherProtocol, which also has 
> someRequiredFunc()? In that case, I want to MyClass as a MyOtherProtocol and 
> get another function pointer, which just happens to have the same 
> human-readable name as some other property. Just because they have the same 
> function signature, absolutely doesn’t mean they’re the same thing.
> 
> Now, if we strongly bind all protocol conformances to the protocol they 
> implement, what happens to instance methods? They don’t belong to any 
> protocol, their parent is the class itself. If you have an instance method 
> called someRequiredFunc(), and you later add a conformance to MyProtocol, you 
> would need to declare that it belongs to MyProtocol. If you don’t want it to 
> be an API-breaking change, you have to provide a thunk (or we could provide a 
> shorthand syntax which emits thunks for you) to let us know that 
> MyClass::someRequiredFunc() is the same thing as 
> MyClass::MyProtocol::someRequiredFunc().
> 
> Your argument is that two methods with the same name should not in any way 
> conflict with each other. This is a fundamental change from the status quo. 
> If we were to take your argument to its logical conclusion, any member A of a 
> type T should be capable of being designated as the implementation of a 
> requirement B of protocol P. In the most general case, two functions A and B 
> shouldn't even need to take the same number of arguments, or arguments of the 
> same type; you should be able to supply default arguments, or even write 
> custom code that takes arguments for A and computes suitable arguments for B 
> in order to forward A to B, and the language should allow you to designate A 
> as an implementation of B. But that is simply not how Swift protocols are 
> designed.
>  
> Let’s take an example where retroactive modelling could go wrong. You’ve got 
> different teams working on different parts of an App, and they’ve all got 
> their own convention for “copy()”. Sometimes it’s a deep-copy, sometimes a 
> shallow-copy, sometimes it’s used in a fragile way for a specific case, 
> whatever. Now you want to go and clean that up by creating a “Copyable” 
> protocol with codified guarantees. Some objects may already conform, others 
> may need tweaks, and some may want both behaviours simultaneously (preserving 
> the old, non-Copytable-compliant behaviour until the next API break), 
> depending on how you look at the object. A system like this allows all of 
> those different ways of looking at the object live together. You could have 
> the old, non-comforming API as an extension with a FIXME to delete it for 
> version 2.
> 
> Even if you design a protocol called Copyable, you still need to explicitly 
> extend concrete types in order to conform to Copyable. Swift does not 
> automagically make anything conform to your protocol. If you choose 
> *explicitly* to conform different types that don't guarantee the same 
> semantics, and then you erroneously assume that they all have the same 
> semantics even though you *explicitly* chose types that don't have the same 
> semantics, you're the one who shot yourself in the foot, so to speak. It's 
> not the fault of Swift at all.
> 
> 
> I think it’s pretty arcane that members of a type are resolved only by their 
> names. If you want to provide types which allow flexible views of data, each 
> view of that data needs to be completely free in its expressivity.
> 
> I would actually like to see a syntax like:
> 
> ```
> let testObject = MyClass()
> let testMyProto = testObject.MyProtocol // the protocol-witness table for 
> testObject as a MyProtocol.
> 
> testObject.MyProtocol.someRequiredFunc() // that’s one function
> testObject.someRequiredFunc() // is a different function. May happen to have 
> the same implementation as above if MyProtocol was retroactively modelled.
> ```
> 
> I think it would fit well with the dispatch system for protocol extensions, 
> too. I sometimes have code like the following:
> 
> ```
> protocol Base {}
> protocol Derived : Base {}
> 
> extension Base { 
>   func doSomething() { … }
> }
> extension Derived {
>   func doSomething() {
>    …
>    (self as Base).doSomething() // Would be better if we could say 
> “self.Base.doSomething()” to disambiguate instead of casting.
>   }
> }
> ```
> 
> This is a complete redesign of protocols in Swift. With the emphasis on 
> minimizing source-breaking changes, I doubt such a change would be in scope 
> for any phase of Swift unless you could show an overwhelming benefit.
> 
> So yeah, a big +1 to marking protocol methods with their protocol (whatever 
> the syntax ends up looking like), and actually I’d take it further and bake 
> them in to the ABI. That also makes it relevant for Swift 4 phase 1.
> 
> Karl

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to