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 <[email protected]> wrote: > > On Fri, Sep 16, 2016 at 20:28 Karl <[email protected] > <mailto:[email protected]>> wrote: >> On 17 Sep 2016, at 01:45, Xiaodi Wu via swift-evolution >> <[email protected] <mailto:[email protected]>> 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 <[email protected] >> <mailto:[email protected]>> wrote: >> On Sep 16, 2016, at 4:08 PM, Xiaodi Wu via swift-evolution >> <[email protected] <mailto:[email protected]>> 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 >> [email protected] <mailto:[email protected]> >> 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 [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
