[swift-evolution] [Proposal Draft] automatic protocol forwarding

Matthew Johnson matthew at anandabits.com
Tue Dec 29 16:22:49 CST 2015



Sent from my iPad

> On Dec 29, 2015, at 4:11 PM, Joe Groff <jgroff at apple.com> wrote:
> 
> 
>> On Dec 29, 2015, at 1:24 PM, Matthew Johnson via swift-evolution <swift-evolution at swift.org> wrote:
>> 
>> Hi Kevin,
>> 
>> Thanks for taking time to look at the proposal.
>> 
>> The technique you show here is not bad, but it has several deficiencies IMO which are addressed by the solution in the proposal.
>> 
>> 1. Forwarding should be an implementation detail, not exposed as it is with this method.
> 
> This could theoretically be managed by access control on protocol conformances:
> 
> public struct Foo: internal SequenceTypeForwarder, public SequenceType { ... }
> 
> though that's even more boilerplatey, and makes it easy to accidentally expose more API than you intended to.

Yep.  I'd like to see access control on protocol conformances for other reasons.  I've always figured that would be accomplished by allowing access control on extensions that declare protocol conformance rather than directly in the conformance itself.  Both seem like reasonable approaches and aren't mutually exclusive.

I also brought up in point #3:

> There is no way to specify access control for the synthesized methods and if there were it would be coupled to the access control of the conformance.


The coupling in this approach would still exist even with access control on conformances.  The synthesized methods would always have the same visibility as the conformance, which may not always be desired.

We've talked in other threads about protocol forwarding a couple of times.  Do you have any general reaction to the approach in this proposal?


> 
> -Joe
> 
>> 2. As such, the getter for the forwardee is visible.  The forwardee is an implementation detail that should not be visible in most cases.
>> 3. There is no way to specify access control for the synthesized methods and if there were it would be coupled to the access control of the conformance.
>> 4. You can't forward to a type that doesn't conform to the protocol.  This is particularly important for existential forwardees (at least until they conform to their protocol).
>> 5. A type that can't conform to the protocol can't forward an implementation of the members of the protocol.  Specifically, non-final classes cannot automatically forward a group of methods to an implementer.
>> 6. This solution does not lay the foundation for a newtype feature. It would be possible to have a specialized newtype feature, but why not build it on top of a more general forwarding feature?
>> 
>> You may be right that many protocols are not amenable to forwarding.  The motivation for this proposal is that it enables delegation-based designs to be implemented succinctly.  In that use case the protocols will be designed alongside concrete implementations and types that forward to them.  A secondary motivation for this proposal is to lay a foundation for a newtype feature.  In that case the protocols to be forwarded would be specifically designed to represent the portion of the interface of the wrapped type which should be visible to users of the newtype.
>> 
>> I hope these points might be enough to convince you that it is worth a closer look.
>> 
>> Matthew
>> 
>>> On Dec 29, 2015, at 2:06 PM, Kevin Ballard via swift-evolution <swift-evolution at swift.org> wrote:
>>> 
>>> I briefly skimmed your proposal, so I apologize if you already addressed this, but it occurs to me that we could support automatic protocol forwarding today on a per-protocol basis simply by declaring a separate protocol that provides default implementations doing the forwarding. Handling of Self return types can then be done by adding a required initializer (or just not implementing that method, so the concrete type is forced to deal with it even though everything else is forwarded).
>>>  
>>> For example, if I want to automatically forward SequenceType to a member, I can do something like
>>>  
>>> protocol SequenceTypeForwarder : SequenceType {
>>>     typealias ForwardedSequenceType : SequenceType
>>> 
>>>     var forwardedSequence : ForwardedSequenceType { get }
>>> }
>>> 
>>> extension SequenceTypeForwarder {
>>>     func generate() -> ForwardedSequenceType.Generator {
>>>         return forwardedSequence.generate()
>>>     }
>>> 
>>>     func underestimateCount() -> Int {
>>>         return forwardedSequence.underestimateCount()
>>>     }
>>> 
>>>     func map<T>(@noescape transform: (ForwardedSequenceType.Generator.Element) throws -> T) rethrows -> [T] {
>>>         return try forwardedSequence.map(transform)
>>>     }
>>> 
>>>     func filter(@noescape includeElement: (ForwardedSequenceType.Generator.Element) throws -> Bool) rethrows -> [ForwardedSequenceType.Generator.Element] {
>>>         return try forwardedSequence.filter(includeElement)
>>>     }
>>> 
>>>     func forEach(@noescape body: (ForwardedSequenceType.Generator.Element) throws -> Void) rethrows {
>>>         return try forwardedSequence.forEach(body)
>>>     }
>>> 
>>>     func dropFirst(n: Int) -> ForwardedSequenceType.SubSequence {
>>>         return forwardedSequence.dropFirst(n)
>>>     }
>>> 
>>>     func dropLast(n: Int) -> ForwardedSequenceType.SubSequence {
>>>         return forwardedSequence.dropLast(n)
>>>     }
>>> 
>>>     func prefix(maxLength: Int) -> ForwardedSequenceType.SubSequence {
>>>         return forwardedSequence.prefix(maxLength)
>>>     }
>>> 
>>>     func suffix(maxLength: Int) -> ForwardedSequenceType.SubSequence {
>>>         return forwardedSequence.suffix(maxLength)
>>>     }
>>> 
>>>     func split(maxSplit: Int, allowEmptySlices: Bool, @noescape isSeparator: (ForwardedSequenceType.Generator.Element) throws -> Bool) rethrows -> [ForwardedSequenceType.SubSequence] {
>>>         return try forwardedSequence.split(maxSplit, allowEmptySlices: allowEmptySlices, isSeparator: isSeparator)
>>>     }
>>> }
>>>  
>>> With this protocol declared, I can then say something like
>>>  
>>> struct Foo {
>>>     var ary: [Int]
>>> }
>>> 
>>> extension Foo : SequenceTypeForwarder {
>>>     var forwardedSequence: [Int] { return ary }
>>> }
>>>  
>>> and my struct Foo now automatically implements SequenceType by forwarding to its variable `ary`.
>>>  
>>> The downside to this is it needs to be manually declared for each protocol. But I wager that most protocols actually aren't really amenable to forwarding anyway.
>>>  
>>> -Kevin Ballard
>>> 
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151229/52ce92a4/attachment.html>


More information about the swift-evolution mailing list