+1 Sent from my iPhone
> On 19 Sep 2016, at 18:10, Vladimir.S via swift-evolution > <swift-evolution@swift.org> wrote: > >> On 17.09.2016 6:32, Xiaodi Wu via swift-evolution wrote: >> >> Let me give a concrete example of how retroactively modeling is used. > > Karl is suggesting interesting but complex and IMO too much code-breaking > idea that I don't believe can be implemented at all in a reasonable amount of > time to be a part of Swift as soon as possible, to address the discussed > issue with protocols. > > I wonder what objections could be made on the solution proposed below, which > should solve a major(IMO) number of issues with protocol conformance and > introduce only 1 keyword. Such solution will make Swift better as > Protocol-Oriented language and later we can even improve it, but it can > already solve a big number of issues: > > 1. As soon as possible we add 'implement' keyword which is required to mark > method/property that was defined in type or extension exactly to conform to > some protocol. > > 2. The 'implement' required only at a moment of 'direct' conformance, i.e. > when you declare methods/props of the type/extension that explicitly > conformed to protocol. > > 3. Retrospective conformance will not require this keyword and will work for > now just like it is working today. > > 4. Later, if this will be possible at all, we can extend this model to > support separate implementation of protocols with same requirements in the > same type, explicit protocol name in implemented methods/props and > improvements for retrospective conformance. For example some variants for > *future* improvements: > > 4.1 Different implementation for different protocols > class Foo : ProtocolA, ProtocolB { > implement(ProtocolA) func foo() {...} > implement(ProtocolB) func foo() {...} > } > class Foo : ProtocolA, ProtocolB { > implement ProtocolA { > func foo() {...} > } > implement ProtocolB { > func foo() {...} > } > } > etc > > 4.2 Retrospective conformance: What is the main problem with retrospective > conformance? As I see it now(correct me, if I missing something), the problem > arises in such situation: > * we *expect* that some method(s) in type will play the role of > implementation of protocol's requirements, so we retrospectively conform that > type to the protocol. > * but protocol has default implementation for its requirements > * and type's methods, that we *expect* to play roles for protocol > implementation, has different parameters or slightly different method name at > all. > > I.e. when we have this set of code logic: > > type T { > func foo() > } > > protocol P { > func foo(x: Int) > } > > extension P { > func foo(x: Int) {...} > } > > extension T : P { // expect foo in T will play role of P.foo > } > > I support the opinion that it is not an option to require to explicitly list > conformed methods/props in type extension for retrospective conformance. > But I do believe we need a way to *express our intention* regarding the > retrospective conformance: do we expect that type already contains > implementation for some protocol's requirements OR we are aware that protocol > can have defaults for some methods and our type does not contains some > implementations. > > So, the solution here IMO is some syntax to express that intention. Right now > I think that we can use current syntax "extension T : P" to keep it working > as it now works: "I'm aware of all the names, defaults etc. Treat this as > usually you did". But for example something like "extension T: implement P > {..}" or "extension T: P(implement *) {..}" will say that we *expect* that > all requirements of P protocol should be implemented inside T type. Or some > syntax inside extension to specify the list of methods/props we expect to be > implemented in T. Or "extension T : P(implement foo, bar(x:y:)) {..}".. > Should be discussed. > > But again, IMO this could be discussed later, after we'll have 'implement' > for most important place - in type definition for method/prop that we created > exactly for the conformed protocol. > > Opinions? > >> 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 >> > _______________________________________________ > swift-evolution mailing list > swift-evolution@swift.org > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution