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

plx plxswift at icloud.com
Wed Dec 30 13:14:45 CST 2015


> On Dec 30, 2015, at 10:27 AM, Matthew Johnson <matthew at anandabits.com> wrote:
> 
> 
>> On Dec 30, 2015, at 10:06 AM, plx via swift-evolution <swift-evolution at swift.org> wrote:
>> 
>> Thanks for writing this up.
> 
> Thanks for responding with comments.
> 
>> 
>> Some quick points.
>> 
>> Firstly, I think it is best if the `init(_ forwardeeReturnValue: Forwardee)`-style initializer be replaced by something with a distinctly-named argument, e.g. `init(forwardeeReturnValue value: Forwardee)`. 
>> 
>> For use with actual wrapper types the “init(_ wrappedValue: Wrapped)`-style init is too valuable to “claim” for this purpose (in particular b/c we may want to “adjust" the forwarded result); it’s IMHO better if we use a distinct `init` for the forwardee-return-value scenario so we know where the value is coming from.
> 
> Did you look at the approach Brent suggested yesterday?  I’m going to update the proposal using something similar to that.  We won’t be claiming any init at all.  It will be slightly less concise in the common case but will add clarity, expressiveness, and resolve problems related to forwarding to more than one member of the same type.

I did not but will make sure to read it.

> 
>> 
>> Secondly, I would prefer partial-forwarding be given more consideration, b/c it seems somewhat common for me in practice at this time.
> 
> If somebody from the core team was enthusiastic about including partial forwarding in the initial proposal rather than as an enhancement that would certainly motivate me to reconsider.
> 
> As I stated earlier and in the proposal, one reason I left it out is that partial forwarding introduces similar concerns that arise with subclassing.  Should a protocol or a type designed to be a forwardee be able to include annotations indicating that some members cannot be “overriden” by a forwarder?  I haven’t had time to fully consider whether that is a good idea or not and if it is, what it would look like, how it should work, etc.  I am reluctant to introduce partial forwarding without really thinking this through.
> 
>> 
>> EG: I would do the following somewhat frequently:
>> 
>> struct FooIdentifier: Equatable, Comparable, Hashable
>> 
>> class Foo {
>> let identifier: FooIdentifier
>> let name: String
>> 
>> forward Hashable to identifier
>> }
>> 
>> func ==(lhs: Foo, rhs: Foo) -> Bool {
>> return lhs.identifier == rhs.identifier && lhs.name == rhs.name
>> }
>> 
> 
> Thanks for providing an example!  It looks like you would have two different instances representing the same entity, possibly one serving as an edit buffer.  Is that why they might have the same identifier but different names?  

This is suffering from the “short examples will necessarily feel contrived, long examples are too long to be polite” problem. Sometimes it’s just from laziness — a good composite `hashValue` can be hard to write, so why not stick with a known-good `hashValue` implementation? — and often makes sense in context (if you know that same-ID, logically-different objects will be rare enough to have negligible overall impact).

I totally agree though I’m getting essentially nothing from forwarding here.

Similar scenarios can show up somewhat *artificially* in the context of generic code. Here’s a somewhat-contrived example:

Suppose you want to write a generic function that can look an “old” array and a “new” array and infer an “edit sequence” that can be applied to transform the “old” one into the “new” one, with an added complication: some values in the “new” one are *revisions* of values from the old one (and thus !=, but still “representing the same thing”). 

This could be implemented a lot of different ways; one of them might look like this: 

	protocol RecordIdentifierType : Equatable, Comparable, Hashable {} // etc
	protocol IdentifiableRecordType : Equatable, Hashable {
	  typealias Identifier: RecordIdentifierType
	  typealias Data: Equatable // weakest guarantee we need for what we plan to use this for
	  // deliberately-clunky names to avoid accidental collisions:	
	  var recordIdentifier: Identifier { get }
	  var recordData: Data { get }
	}

	func inferredEditOperations<R:IdentifiableRecordType>(
	  forPreviousRecords previousRecords: [R],
	  currentRecords: [R]) -> ArrayEditOperation {
	  // exercise for reader
        }

…wherein 99% of the time a typical concrete conformance would just return `self` as `recordData` and some appropriate property as `recordIdentifier`, but it’d still be handy to have an “adaptor" type laying around like this:

	struct IdentifiableRecordAdaptor<I:RecordIdentifierType,D:Equatable> : IdentifiableRecordType {
	  typealias Identifier = I
	  typealias Data = D
	  let recordIdentifier: I
	  let recordData: D

	  forwarding Hashable to RecordIdentifier
	}

…to support broader use of the generic algorithm in certain cases.

Note that this is still quite contrived — and it's still just `Equatable`/`Hashable` — but I think you can see how the scenario here could generalize in other contexts.

But at the same time, perhaps partial-forwarding is actually more of a niche use here than I thought?

> 
>> …even though I agree that full-forwarding would the most-common scenario.
>> 
>> I have a few other similar cases involving non-standard-library types but they all fit the same basic pattern as above for `Hashable`.
>> 
>> Finally, I’m actually at a bit of a loss for too many uses for the generic forwarding mechanism; if it was added I’d use it to streamline some wrapper types, but beyond that I’m not seeing all that many places where I’d do much more than that with this feature if added.
>> 
>> Any chance at adding a few more-concrete motivating examples for the fancier cases?
> 
> Yes, this is needed and I am working on it now.
> 
>> 
>>> On Dec 29, 2015, at 10:37 AM, Matthew Johnson via swift-evolution <swift-evolution at swift.org> wrote:
>>> 
>>> I have completed a first draft of a proposal to introduce automatic protocol forwarding.  I’m looking forward to feedback from everyone!
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 



More information about the swift-evolution mailing list