[swift-evolution] Mark protocol methods with their protocol

Vladimir.S svabox at gmail.com
Fri Sep 23 11:25:05 CDT 2016


On 23.09.2016 17:41, Rien wrote:
> Hello Vladimir,
>
> Going back to the original suggestion:
>
> /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() {...}/
> /   }/
> /}/
>
> I would rather solve this by implementing the conformance of Foo to either
> protocol in its own extension:

Yes, this solution also was discussed in one of last emails. More 
specifically, the solution where only warning will be raised and only if 
you conform to protocol in extension(if you conform "in-place" - no any 
additional feature to express your intention) was discussed.

Will repeat myself:

* Currently extension can not have stored properties. So, if we want to 
implement protocol's props as stored properties - we can't move them to 
extension. So to implement this soulution - we need stored properties in 
extensions. It is not clear if and when they are expected.

* This solution will not require the safety(regarding protocol conformance) 
from a developer, it will only inform and only if protocol conformance 
defined in extension. So, when you use 3rd party source code - your project 
will not be protected for the discussed problems.

* To write safe code I can't group methods/props as I want, I have to 
declare a number of extensions per-protocol (in case my type conforms to a 
number of protocols)

* This solution does not solve problem of near-miss signature of method 
definition in protocol extension like here:
protocol A { func foo() }
protocol B : A {}
extension A { func foo() }
extension B { func voo() } // typo. how to "mark" this should be impl?

* Not clear how to write safe code with that approach if we implement 
protocol requirement in derived class, but conformance was declared in base 
(but not implemented) :
protocol P { func foo() }
extension P { func foo() }
class A : P {}
class B { func foo() } // we can't move this to extension, B already 
conforms to P
, and in opposite to "my" `override` requirement for implementation, if `A` 
will add its own foo() implementation - we'll have to change B's 
definition(need to add `override` for B.foo )

If these questions could be addressed by the "conformance in extension" 
solution - then I also most likely will think it is the best solution. But 
right now I see that this solution will not help in many places where we 
need help from compiler to prevent hard-to-find bugs.

>
> class Foo {}
>
> extension Foo: ProtocolA {
>  func foo()
> }
>
> extension Foo: ProtocolB {
>  func foo()
> }
>
> This way the extra keyword is not necessary.
> However it does make it necessary to be able to select the exact foo() an
> API user would want.
> The API user has that knowledge and can select if via: (Foo as!
> ProtocolA).foo()
>
> Or via a convenience dot-contruction: Foo.ProtocolA.foo()
>
> I think that my suggestion may be more work, but it feels to me as if it is
> more in line with the general swift-phylosophy.
>
> Regards,
> Rien.
>
>
>
>
>> On 23 Sep 2016, at 15:08, Vladimir.S <svabox at gmail.com
>> <mailto:svabox at gmail.com>> wrote:
>>
>> On 23.09.2016 15:10, Rien wrote:
>>> Note: Second attempt, first mail was rejected ?
>>>
>>> That is correct Vladimir, at the point of writing an API you never know
>>> who will end up using it in which way.
>>> Hence the decision which flavour (of a function) to call should not be
>>> made by the coder writing the API but by the coder using the API.
>>> And that coder cannot claim not to know which flavour he wants.
>>> Hence including the ‘override’ keyword is unnecessary. However having
>>> the ability to specifying which flavour must be called is necessary. And
>>> this ability is easy to accommodate within the current language rules.
>>> (casting or possibly having a dot-notation for this purpose)
>>
>> Sorry, I'm not sure I fully understand.. I replied to Maximilian's
>> suggestion to use `conform` keyword if there is no default implementation
>> for implemented protocol requirement and use `override` if there is such
>> default implementation for the requirement. So, I show that we can't
>> accept such solution as it leads to such kind of dependency when some
>> other's code depends on your local code. I.e. if you add default
>> implementations for protocol defined in 3rd party code - you can break
>> compilation of that code and this should not happen(see my example in
>> previous message).
>>
>> As for requirement for any keyword to mark protocol implementation
>> method/prop in type - there were a lot of description in the thread why
>> we need this and what kind of problem this could solve and prevent
>> hard-to-find bugs. Also please look into initial problem of this thread.
>>
>>>
>>> Regards,
>>> Rien.
>>>
>>>> On 23 Sep 2016, at 12:47, Vladimir.S via swift-evolution
>>>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>
>>>> On 23.09.2016 11:05, Maximilian Hünenberger wrote:
>>>>> I'd also say that one or two keywords are superior than the protocol
>>>>> naming
>>>>> approach in terms of implementation simplicity (for the core team).
>>>>>
>>>>> My suggestions:
>>>>>
>>>>> Either "conform" or "implement" should be a required keyword for all
>>>>> properties/functions which implement a protocol (also in protocol
>>>>> extensions)
>>>>>
>>>>
>>>>> "override" should be used if a default implementation or a member of a
>>>>> superclass is overridden.
>>>>
>>>> Maximilian, again, you *do not know* if the conformed protocol, that
>>>> has no default implementations *at the moment of your code writing*
>>>> will or will not have default implementations at the *moment of
>>>> compilation*.
>>>> Consider this scenario:
>>>>
>>>> Step 1. You got 3rd party source file for your project, and you don't
>>>> want/have no rights to change it, probably it is shared source used
>>>> also in other projects, that code contains:
>>>>
>>>> protocol A { func foo() }
>>>>
>>>> class B : A {
>>>> conform func foo() {...}
>>>> }
>>>>
>>>> all is OK with this code, no default implementation, B.foo marked with
>>>> `conform`.
>>>>
>>>> Step 2. In your project in some of your files you decided to add
>>>> default implementation of protocol A:
>>>>
>>>> extension A {
>>>> implement func foo() {...}
>>>> }
>>>>
>>>> Now, your project will not compile - B.foo() must me marked with
>>>> 'override' as protocol `A` has default implementation of foo().
>>>> If you change `conform` to `override` in 3rd party source file, it will
>>>> not compile in some other project where no default implementation
>>>> defined for `A` protocol.
>>>>
>>>> That is *why* I believe the `override` as requirement as marker for
>>>> protocol implementation method/prop is the best solution. See, in case
>>>> `override` will be required, the initial source file will be like this:
>>>>
>>>> protocol A { func foo() }
>>>>
>>>> class B : A {
>>>> override func foo() {...} // implementation
>>>> }
>>>>
>>>> and it will compile ok : if A has default implementation and if A has
>>>> no default implementation.
>>>>
>>>> So, after you added default implementation in your project - no changes
>>>> should be made to that 3rd party source file.
>>>>
>>>>
>>>>>
>>>>> If you are overriding a default implementation of a protocol "conform" /
>>>>> "implement" is also required.
>>>>>
>>>>> // Retroactive conformance (old behavior) but only in extensions
>>>>> extension Foo: @retroactive Baz {
>>>>>   // only some members of Baz are implemented here (they need the
>>>>> keywords)
>>>>>   // the other members outside the extension don't need any additional
>>>>> keywords
>>>>>   // note: you can use "@retroactive" and "conform" in conjunction
>>>>> }
>>>>>
>>>>>
>>>>> *Future directions:*
>>>>> "conform(ProtocolName)" / "override(ProtocolName)" can be used to
>>>>> disambiguate.
>>>>>
>>>>> // reducing boilerplate
>>>>> extension Foo: conform Bar {
>>>>>   // These declarations can only implement Bar and don't need the
>>>>> "conform" keyword
>>>>> }
>>>>>
>>>>> *Final question:*
>>>>> Should we also require a marker for implemented protocol members in the
>>>>> interface?:
>>>>>
>>>>> protocol Foo {
>>>>>   defaulted func foo()
>>>>> }
>>>>> extension Foo {
>>>>>   implement func foo()
>>>>> }
>>>>>
>>>>>
>>>>> Best regards
>>>>> Maximilian
>>>>>
>>>>> Am 22.09.2016 um 16:44 schrieb Vladimir.S via swift-evolution
>>>>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>>> <mailto:swift-evolution at swift.org>>:
>>>>>
>>>>>> On 22.09.2016 11:10, Jean-Denis Muys via swift-evolution wrote:
>>>>>>> I watched this thread with a lot of attention, starting neutral. You
>>>>>>> must say that Karl won me over. His proposal would make Swift more
>>>>>>> expressive, and less error prone in cases of protocol conformance with
>>>>>>> name collisions. I am at this point +1
>>>>>>
>>>>>> Actually I also support Karl's suggestion in general. It is trying to
>>>>>> solve IMO important problems and make Swift's protocol programming safer
>>>>>> and less fragile. Also it adds new interested features for working with
>>>>>> protocols.
>>>>>>
>>>>>> But in reality, I don't feel like his suggestion could be accepted by
>>>>>> core team and community and even if it could be supported, it seems for
>>>>>> me that *implementation* of his proposal requires a huge amount of time
>>>>>> and big changes in how Swift is working currently. (Probably some one who
>>>>>> knows Swift internals could comment regarding this)
>>>>>> So, theoretically we'd have these improvements not in near future and I
>>>>>> think the problem discussed is very important to be addressed in Swift as
>>>>>> soon as possible.
>>>>>> I base my opinion also on previous discussions regarding similar
>>>>>> subjects.
>>>>>>
>>>>>> My suggestion regarding a marker for protocol implementation method/prop
>>>>>> in type - solves most of the addressed problems with protocol conformance
>>>>>> and with fragile of such conformance, and adds one new keyword (or even
>>>>>> zero - right now I think the `override` is better choice for such
>>>>>> "marker"). I believe this proposal could be implemented with much less
>>>>>> amount of work and with less changes to current internals of Swift and to
>>>>>> current code base, and so we can have such a big improvement in Swift
>>>>>> soon. So my intention was to suggest solution that can dramatically
>>>>>> improve Swift's protocol programming with "small" amount of changes for
>>>>>> compiler(internals) and for existed sources.
>>>>>>
>>>>>> But it seems like the direction chosen by the core team and supported by
>>>>>> many in community - is just a warning if extension conforming type to
>>>>>> protocol contains unrelated to that protocol methods/props. I see that
>>>>>> this solution can improve protocol programming in some areas, but does
>>>>>> not address some IMO important issues we discussed in the thread :
>>>>>>
>>>>>> * Currently extension can not have stored properties. So, if we want to
>>>>>> implement protocol's props as stored properties - we can't move them to
>>>>>> extension. So to implement this soulution - we need stored properties in
>>>>>> extensions. It is not clear if and when they are expected.
>>>>>>
>>>>>> * This solution will not require the safety(regarding protocol
>>>>>> conformance) from a developer, it will only inform and only if protocol
>>>>>> conformance defined in extension. So, when you use 3rd party source code
>>>>>> - your project will not be protected for the discussed problems.
>>>>>>
>>>>>> * To write safe code I can't group methods/props as I want, I have to
>>>>>> declare a number of extensions per-protocol (in case my type conforms to
>>>>>> a number of protocols)
>>>>>>
>>>>>> * This solution does not solve problem of near-miss signature of method
>>>>>> definition in protocol extension like here:
>>>>>> protocol A { func foo() }
>>>>>> protocol B : A {}
>>>>>> extension A { func foo() }
>>>>>> extension B { func voo() } // typo. how to "mark" this should be impl?
>>>>>> "my" suggestion:
>>>>>> extension A { override func foo() }
>>>>>> extension B { override func foo() }
>>>>>>
>>>>>> * Not clear how to write safe code with that approach if we implement
>>>>>> protocol requirement in derived class, but conformance was declared in
>>>>>> base (but not implemented) :
>>>>>> protocol P { func foo() }
>>>>>> extension P { func foo() }
>>>>>> class A : P {}
>>>>>> class B { func foo() } // we can't move this to extension, B already
>>>>>> conforms to P
>>>>>> , and in opposite to "my" `override` requirement for implementation, if
>>>>>> `A` will add its own foo() implementation - we'll have to change B's
>>>>>> definition(need to add `override` for B.foo )
>>>>>> "my" suggestion:
>>>>>> class B { override func foo() }
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> Jean-Denis
>>>>>>>
>>>>>>> Sent from my iPhone
>>>>>>>
>>>>>>>> On 22 Sep 2016, at 07:15, Karl via swift-evolution
>>>>>>>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>>>>>> <mailto:swift-evolution at swift.org>> wrote:
>>>>>>>>
>>>>>>>> I would like to make it a requirement if not inside a protocol
>>>>>>>> extension which declares a conformance, and actually build the
>>>>>>>> protocol name in to the member in an ABI-breaking way. We could make
>>>>>>>> it additive by generating forwarding thunks from the old symbols to
>>>>>>>> the new ones, but it would be better if we could just solve the
>>>>>>>> overlapping-members problem before then.
>>>>>>>>
>>>>>>>> That would mean you never get collisions between protocol members.
>>>>>>>> There’s loads of amazing stuff we can do with that ability, and ways
>>>>>>>> we can extend it to reduce a lot of boilerplate that occurs when you
>>>>>>>> want to have multiple representations of the same data (String is just
>>>>>>>> an example).
>>>>>>>>
>>>>>>>> I don’t really care about the syntax we need to make it liveable. We
>>>>>>>> could automatically insert the protocol names for unambiguous members
>>>>>>>> at call-site, or something else.
>>>>>>>>
>>>>>>>> This thread was originally about making the *syntax* a requirement; I
>>>>>>>> agree with that, and I would actually take it one (or several) steps
>>>>>>>> further, solving other problems along the way.
>>>>>>>>
>>>>>>>>> On 22 Sep 2016, at 06:46, Russ Bishop <xenadu at gmail.com
>>>>>>>>> <mailto:xenadu at gmail.com>
>>>>>>>>> <mailto:xenadu at gmail.com>> wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>> On Sep 20, 2016, at 4:34 PM, Dave Abrahams via swift-evolution
>>>>>>>>>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>>>>>>>> <mailto:swift-evolution at swift.org>> wrote:
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>> on Tue Sep 20 2016, Karl <razielim-AT-gmail.com
>>>>>>>>>>> <http://razielim-at-gmail.com>
>>>>>>>>>>> <http://razielim-AT-gmail.com <http://razielim-at-gmail.com>>>
>>>>>>>>>>> wrote:
>>>>>>>>>>>
>>>>>>>>>>> I think the best way is to prefix the member name with the
>>>>>>>>>>> protocol, e.g:
>>>>>>>>>>>
>>>>>>>>>>> protocol MyProto { var aVariable : Int func aFunction() } class
>>>>>>>>>>> MyClass : MyProto { var MyProto.aVariable : Int func
>>>>>>>>>>> MyProto.aFunction() { … } }
>>>>>>>>>> ...
>>>>>>>>>>> CC-ing Dave A, to understand better if this fits with the vision
>>>>>>>>>>> of protocols
>>>>>>>>>>
>>>>>>>>>> I generally agree with Doug.  The canonical way to indicate “this
>>>>>>>>>> method/property/type implements a requirement of protocol P”
>>>>>>>>>> should be to define the entity in an extension that also adds
>>>>>>>>>> conformance to P. If that's inadequate indication in some way we
>>>>>>>>>> should find a way to enhance it.  I wouldn't mind the notation
>>>>>>>>>> above, but only as a fallback, not a reuquirement.
>>>>>>>>>>
>>>>>>>>>> -- -Dave _______________________________________________
>>>>>>>>>
>>>>>>>>> Indeed this is exactly how C# handles Interfaces (protocols). The
>>>>>>>>> default is the exact same way Swift works - by matching names. If
>>>>>>>>> there is a collision you specify Protocol.memberName. Its simple and
>>>>>>>>> in the years I was writing C# code it was flexible enough to cover
>>>>>>>>> most reasonable scenarios, without adding a bunch of boilerplate.
>>>>>>>>>
>>>>>>>>> Russ
>>>>>>>>>
>>>>>>>>
>>>>>>>> _______________________________________________ swift-evolution
>>>>>>>> mailing list swift-evolution at swift.org
>>>>>>>> <mailto:swift-evolution at swift.org> <mailto:swift-evolution at swift.org>
>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>> _______________________________________________ swift-evolution mailing
>>>>>>> list swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>>>>> <mailto:swift-evolution at swift.org>
>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>>
>>>>>> _______________________________________________
>>>>>> swift-evolution mailing list
>>>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>>>> <mailto:swift-evolution at swift.org>
>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>>
>


More information about the swift-evolution mailing list