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

Kevin Ballard kevin at sb.org
Tue Dec 29 16:29:32 CST 2015


On Tue, Dec 29, 2015, at 01:24 PM, Matthew Johnson 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.
> 2. As such, the getter for the forwardee is visible.  The forwardee is
>    an implementation detail that should not be visible in most cases.

I was tempted to call it `_forwardedSequence` instead, but I chose not
to do that because the _prefix convention right now means "stdlib
things that we don't want to expose but have to do so because of
implementation details".

> 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.

I'm not sure what you mean by this. Access control for these
methods should work identically to access control for the original
protocol. The forwarder protocol would simply be declared with the
same access control.

> 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).

It seems weird to me that your forwarding proposal allows you to forward
to a member that doesn't conform to the protocol. I suppose it makes
some sense if you only forward some methods and implement the others
yourself, but I'm not convinced there's actually a good reason to
support this. What circumstances are you thinking of where you'd
actually find yourself forwarding to a member that doesn't conform to
the protocol (that isn't an existential)? The only case that really
comes to mind is when the member is of a generic type that can't conform
to the protocol (e.g. Array<Int> doesn't conform to Equatable), but the
solution there is to allow for conditional protocol conformance (which
we already know is something we want in the language).

Forwarding to existentials is a valid concern, but I'm not actually sure
why Swift doesn't make existential protocol values conform to the
protocol anyway. That limitation would make sense if you could create
existential protocol values from protocols with Self/associated types
where the existential simply omits the relevant members (because then it
obviously doesn't conform to the protocol), but  Swift doesn't actually
let you create existentials in that situation anyway.

> 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.

It sounds like you're talking here about the ability to forward members
that aren't actually associated with a protocol, right? I don't see why
you can't just declare a protocol in that case to represent the members
that you want to be able to forward.

> 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?

Barring a detailed newtype proposal, it's hard to see how forwarding
would actually interact with it. Are you thinking that a newtype would
have some member that provides the underlying value (e.g. a `var
rawValue`)? Given such a member, a generalized forwarding mechanism
would then interact with it. But if e.g. newtype works by letting you
cast (with `as`) to the underlying type, then your generalized
forwarding mechanism can't actually work unless it has two different
modes (one of which uses a member and the other uses a cast), which
means you're specializing for newtype anyway.

Besides, when using a newtype mechanism like that, since the newtype has
the same in-memory representation as the original type, you don't
actually want to generate new methods at all for forwarding, instead you
just want to re-use the exact same methods as the original type
(otherwise you're just bloating your code with a bunch of stubs that do
nothing more than bitcast the value and call the original method).

-Kevin Ballard

> 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 } }
>>
>> extensionSequenceTypeForwarder { 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] }
>>
>> extensionFoo : 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151229/d81b7fbf/attachment.html>


More information about the swift-evolution mailing list