Renaming for Protocol Conformance


(Jon Hull) #1

Hi everyone,

We talked about this before when we were discussing mixins, and there seemed to be generally positive feelings towards it as a feature for the future. I am fairly certain this affects the ABI though, so I thought I would bring it up now.

If two protocols have methods/properties with the same name, but different signatures, we need a way to distinguish between them when attempting to conform to both.

  protocol A {
    var x:Int {get set}
  }

  protocol B {
    var x:Double {get set}
  }

One possibility is to allow a struct/class/enum to conform to the protocol while renaming one (or both) of the clashing methods:

  struct C: A,B {
    var x:Int
    var y:Double implements B.x
  }

The conforming method/property would still have to have the same signature, but could have a different name (and parameter labels). It would also allow protocol methods which have identical signatures and semantics, but different names to be implemented using the same method (i.e ‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the property (e.g. ‘x’) would end up calling the implementation of the renamed property ( ‘y’ in this example) on the conforming type.

I think we would also want a way to retroactively conform using existing properties/methods in an extension declaring conformance. Not sure what the best syntax for that would be. Off the top of my head (though I would love to have something with less cruft):

  extension D:B {
    @conform(to: B.x, with: D.y)
  }

or maybe just:
  
  extension D:B {
    D.y implements B.x
  }
  
All of this is merely to start the discussion, so feel free to propose better syntax or a more elegant solution...

Thoughts?

Thanks,
Jon


(Charles Srstka) #2

I was just thinking about proposing something like this.

+1.

Charles

···

On Aug 22, 2016, at 11:59 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

Hi everyone,

We talked about this before when we were discussing mixins, and there seemed to be generally positive feelings towards it as a feature for the future. I am fairly certain this affects the ABI though, so I thought I would bring it up now.

If two protocols have methods/properties with the same name, but different signatures, we need a way to distinguish between them when attempting to conform to both.

  protocol A {
    var x:Int {get set}
  }

  protocol B {
    var x:Double {get set}
  }

One possibility is to allow a struct/class/enum to conform to the protocol while renaming one (or both) of the clashing methods:

  struct C: A,B {
    var x:Int
    var y:Double implements B.x
  }

The conforming method/property would still have to have the same signature, but could have a different name (and parameter labels). It would also allow protocol methods which have identical signatures and semantics, but different names to be implemented using the same method (i.e ‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the property (e.g. ‘x’) would end up calling the implementation of the renamed property ( ‘y’ in this example) on the conforming type.

I think we would also want a way to retroactively conform using existing properties/methods in an extension declaring conformance. Not sure what the best syntax for that would be. Off the top of my head (though I would love to have something with less cruft):

  extension D:B {
    @conform(to: B.x, with: D.y)
  }

or maybe just:
  
  extension D:B {
    D.y implements B.x
  }
  
All of this is merely to start the discussion, so feel free to propose better syntax or a more elegant solution...

Thoughts?

Thanks,
Jon

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #3

Hi everyone,

We talked about this before when we were discussing mixins, and there
seemed to be generally positive feelings towards it as a feature for the
future.

It's been some time now since the original discussion, so perhaps you could
refresh our collective memory (or at least, mine): although it *seems* like
this feature might be useful, I can't recall a concrete use case where I've
felt like I needed this feature--do you have some examples?

  I am fairly certain this affects the ABI though, so I thought I would
bring it up now.

If two protocols have methods/properties with the same name, but different
signatures, we need a way to distinguish between them when attempting to
conform to both.

        protocol A {
                var x:Int {get set}
        }

        protocol B {
                var x:Double {get set}
        }

Methods can be overloaded that differ in arguments or return type, so it
seems like this problem mainly exists with *properties* that differ in
type--am I wrong?

One possibility is to allow a struct/class/enum to conform to the protocol
while renaming one (or both) of the clashing methods:

        struct C: A,B {
                var x:Int
                var y:Double implements B.x
        }

The conforming method/property would still have to have the same
signature, but could have a different name (and parameter labels). It
would also allow protocol methods which have identical signatures and
semantics, but different names to be implemented using the same method (i.e
‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the
property (e.g. ‘x’) would end up calling the implementation of the renamed
property ( ‘y’ in this example) on the conforming type.

Reflecting on this proposed change, it occurs to me that something of value
would be lost, and I think that this something is actually rather valuable:

Today, when I see that a type conforms to (for example) Sequence, I know
that certain methods and/or properties exist on that type. Protocol
conformance guarantees a certain API, not just certain semantics.

Perhaps one way to mitigate this loss would be to have any renamed members
listed *in the declaration of conformance*, something like this (with some
additional bikeshedding):

struct MyGreatType : Sequence (count => length) {
  // MyGreatType conforms to Sequence but renames `count` to `length`
}

I think we would also want a way to retroactively conform using existing

properties/methods in an extension declaring conformance. Not sure what
the best syntax for that would be. Off the top of my head (though I would
love to have something with less cruft):

        extension D:B {
                @conform(to: B.x, with: D.y)
        }

or maybe just:

        extension D:B {
                D.y implements B.x
        }

If renamed members are declared along with protocol conformance, then the
syntax for retroactive modeling follows naturally:

extension D : B (x => y) { }
// again, the actual notation here is ugly
// but the underlying idea, I think, is worth considering

All of this is merely to start the discussion, so feel free to propose

···

On Mon, Aug 22, 2016 at 11:59 PM, Jonathan Hull via swift-evolution < swift-evolution@swift.org> wrote:

better syntax or a more elegant solution...

Thoughts?

Thanks,
Jon

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Karl) #4

What about if we synthesised members on the conforming type? So I’m thinking

  protocol MyProtoOne {
    func doSomething()
  }

  protocol MyProtoTwo {
    func doSomething()
  }

  struct ImplementsOne : MyProtoOne {
    func doSomething() {
      // Unambiguous. Satisfies MyProtoOne.
    }
  }

  struct ImplementsBoth : MyProtoOne, MyProtoTwo {
    
    func MyProtoOne.doSomething() {
      // Or some other declaration modifier to say this for MyProtoOne
    }

    func MyProtoTwo.doSomething() {
      // ...
    }
  }

ImplementsOne().doSomething() // unambiguous intention at call-site
ImplementsBoth().MyProtoOne.doSomething() // intention at call-site must be explicit. What kind of thing do you want to look at ImplementsBoth() as?
ImplementsBoth().MyProtoTwo.doSomething()

I think this would be relatively simple to implement. We just disambiguate at declaration site, and the synthesised members ImplementsBoth().MyProto{One/Two} are the protocol-witness tables to disambiguate at the call-site.

The thing is that we would need to mangle those names differently, and for ABI resilience (the ability to add a protocol with a conflicting name later) that would mean also mangling the protocol name in to the non-conflicting cases (ImplementsOne.MyProtoOne.doSomething). That means if you had ImplementsOne.doSomething in v1 and added the MyProtoOne protocol in v1.1, you would need to add a plain doSomething() method which doesn’t belong to a protocol.

That’s the thing — members would kind of “belong” to the protocol conformance on the type, rather than being plain top-level members on the type itself (although as long as there are no conflicts, you could use them as such and the compiler would know which members you mean). That may be beneficial for mixins.

Karl

···

On 23 Aug 2016, at 06:59, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

Hi everyone,

We talked about this before when we were discussing mixins, and there seemed to be generally positive feelings towards it as a feature for the future. I am fairly certain this affects the ABI though, so I thought I would bring it up now.

If two protocols have methods/properties with the same name, but different signatures, we need a way to distinguish between them when attempting to conform to both.

  protocol A {
    var x:Int {get set}
  }

  protocol B {
    var x:Double {get set}
  }

One possibility is to allow a struct/class/enum to conform to the protocol while renaming one (or both) of the clashing methods:

  struct C: A,B {
    var x:Int
    var y:Double implements B.x
  }

The conforming method/property would still have to have the same signature, but could have a different name (and parameter labels). It would also allow protocol methods which have identical signatures and semantics, but different names to be implemented using the same method (i.e ‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the property (e.g. ‘x’) would end up calling the implementation of the renamed property ( ‘y’ in this example) on the conforming type.

I think we would also want a way to retroactively conform using existing properties/methods in an extension declaring conformance. Not sure what the best syntax for that would be. Off the top of my head (though I would love to have something with less cruft):

  extension D:B {
    @conform(to: B.x, with: D.y)
  }

or maybe just:
  
  extension D:B {
    D.y implements B.x
  }
  
All of this is merely to start the discussion, so feel free to propose better syntax or a more elegant solution...

Thoughts?

Thanks,
Jon

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #5

Hi everyone,

We talked about this before when we were discussing mixins, and there seemed to be generally positive feelings towards it as a feature for the future. I am fairly certain this affects the ABI though, so I thought I would bring it up now.

If two protocols have methods/properties with the same name, but different signatures, we need a way to distinguish between them when attempting to conform to both.

  protocol A {
    var x:Int {get set}
  }

  protocol B {
    var x:Double {get set}
  }

I believe that this can happen, and it is unfortunate that Swift has no mechanism for dealing with it today. However, I agree with Xiaodi that your proposal would be much stronger with real-world examples rather than theoretical ones.

One possibility is to allow a struct/class/enum to conform to the protocol while renaming one (or both) of the clashing methods:

  struct C: A,B {
    var x:Int
    var y:Double implements B.x
  }

The conforming method/property would still have to have the same signature, but could have a different name (and parameter labels). It would also allow protocol methods which have identical signatures and semantics, but different names to be implemented using the same method (i.e ‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the property (e.g. ‘x’) would end up calling the implementation of the renamed property ( ‘y’ in this example) on the conforming type.

Sure. Calling through the protocol type will get whatever method/property satisfied the protocol requirement. Yes, there are limits here due to protocols with associated types and Self requirements, but I fully expect those to go away at some point with generalized existentials.

I think we would also want a way to retroactively conform using existing properties/methods in an extension declaring conformance. Not sure what the best syntax for that would be. Off the top of my head (though I would love to have something with less cruft):

  extension D:B {
    @conform(to: B.x, with: D.y)
  }

or maybe just:
  
  extension D:B {
    D.y implements B.x
  }
  
All of this is merely to start the discussion, so feel free to propose better syntax or a more elegant solution...

C# has a much narrower solution that lets you qualify the method declaration (rather than doing a full rename), e.g.,

  struct C : A {
    var x: Int
    var y: Double
  }

  extension C : B {
    var B.x: Double {
      get { return y }
      set { y = newValue }
    }
  }

They have some examples at:

  https://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx <https://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx>

One would have to figure out what the name-lookup rules are, of course, but this might allow us to solve the problem without introducing a generalized renaming mechanism.

  - Doug

···

On Aug 22, 2016, at 9:59 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:


(Jon Hull) #6

Hi everyone,

We talked about this before when we were discussing mixins, and there seemed to be generally positive feelings towards it as a feature for the future.

It's been some time now since the original discussion, so perhaps you could refresh our collective memory (or at least, mine): although it *seems* like this feature might be useful, I can't recall a concrete use case where I've felt like I needed this feature--do you have some examples?

Ideally, the biggest use is that it helps to (partially) solve the diamond problem (and similar issues) by forcing/allowing disambiguation when there are multiple protocols being conformed to. This will become more of an issue if we allow protocols or extensions to add storage. Your proposed syntax actually does a better job of it than mine because mine was always shown as attached to some sort of implementation, whereas yours could potentially allow access to a default implementation under a new name.

Other than that, it generally allows us to bypass/mitigate conflicts between protocols. In the current version, you are unable to conform to both protocols (either because it won’t compile or because you can’t satisfy the semantics of both protocols) without designing the protocols together to avoid conflicts. (I have definitely had to go back and rename/refactor properties on a protocol for this reason… which I couldn’t have done if I didn’t control both protocols).

Take a look at Eiffel’s ‘rename’ & ’select’ features for similar functionality and use-cases.

Ultimately, this is a step in the direction of having true mixins.

   I am fairly certain this affects the ABI though, so I thought I would bring it up now.

If two protocols have methods/properties with the same name, but different signatures, we need a way to distinguish between them when attempting to conform to both.

        protocol A {
                var x:Int {get set}
        }

        protocol B {
                var x:Double {get set}
        }

Methods can be overloaded that differ in arguments or return type, so it seems like this problem mainly exists with *properties* that differ in type--am I wrong?

There is also the case of functions with the same name and signature, but different semantics. There may be no single implementation which simultaneously satisfies the semantics for both protocols. By renaming one of the functions, we are able to provide separate implementations for each requirement (which allows both protocols to function as intended).

There may also be functions in different protocols with different names but the same semantics and signature. This will allow a single implementation to satisfy both protocols without duplication.

Finally, we may want to rename an inherited default implementation to avoid conflicting with another protocol's default implementation in cases where we don’t want to override it.

One possibility is to allow a struct/class/enum to conform to the protocol while renaming one (or both) of the clashing methods:

        struct C: A,B {
                var x:Int
                var y:Double implements B.x
        }

The conforming method/property would still have to have the same signature, but could have a different name (and parameter labels). It would also allow protocol methods which have identical signatures and semantics, but different names to be implemented using the same method (i.e ‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the property (e.g. ‘x’) would end up calling the implementation of the renamed property ( ‘y’ in this example) on the conforming type.

Reflecting on this proposed change, it occurs to me that something of value would be lost, and I think that this something is actually rather valuable:

Today, when I see that a type conforms to (for example) Sequence, I know that certain methods and/or properties exist on that type. Protocol conformance guarantees a certain API, not just certain semantics.

It isn’t actually lost, however. When working with it as a Sequence (for example), that API would be intact using the original names. It is only when working with it as its own type that the renaming would have an effect.

Perhaps one way to mitigate this loss would be to have any renamed members listed *in the declaration of conformance*, something like this (with some additional bikeshedding):

struct MyGreatType : Sequence (count => length) {
  // MyGreatType conforms to Sequence but renames `count` to `length`
}

Yes, putting it in the conformance declaration is a definite possibility we should consider.

I think we would also want a way to retroactively conform using existing properties/methods in an extension declaring conformance. Not sure what the best syntax for that would be. Off the top of my head (though I would love to have something with less cruft):

        extension D:B {
                @conform(to: B.x, with: D.y)
        }

or maybe just:

        extension D:B {
                D.y implements B.x
        }

If renamed members are declared along with protocol conformance, then the syntax for retroactive modeling follows naturally:

extension D : B (x => y) { }
// again, the actual notation here is ugly
// but the underlying idea, I think, is worth considering

Yup

One thing I like about this is that it helps to solve the diamond problem. ‘x’ could be a default implementation in B which D does not override. I think this is an important case which my original proposal didn’t address fully.

We should keep bikeshedding the syntax though...

Thanks,
Jon

···

On Aug 22, 2016, at 11:32 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Mon, Aug 22, 2016 at 11:59 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:


(Karl) #7

Actually, reading the next thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160822/026524.html

This could even be a feature.

Karl

···

On 23 Aug 2016, at 07:39, Karl <raziel.im+swift-evo@gmail.com> wrote:

On 23 Aug 2016, at 06:59, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

Hi everyone,

We talked about this before when we were discussing mixins, and there seemed to be generally positive feelings towards it as a feature for the future. I am fairly certain this affects the ABI though, so I thought I would bring it up now.

If two protocols have methods/properties with the same name, but different signatures, we need a way to distinguish between them when attempting to conform to both.

  protocol A {
    var x:Int {get set}
  }

  protocol B {
    var x:Double {get set}
  }

One possibility is to allow a struct/class/enum to conform to the protocol while renaming one (or both) of the clashing methods:

  struct C: A,B {
    var x:Int
    var y:Double implements B.x
  }

The conforming method/property would still have to have the same signature, but could have a different name (and parameter labels). It would also allow protocol methods which have identical signatures and semantics, but different names to be implemented using the same method (i.e ‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the property (e.g. ‘x’) would end up calling the implementation of the renamed property ( ‘y’ in this example) on the conforming type.

I think we would also want a way to retroactively conform using existing properties/methods in an extension declaring conformance. Not sure what the best syntax for that would be. Off the top of my head (though I would love to have something with less cruft):

  extension D:B {
    @conform(to: B.x, with: D.y)
  }

or maybe just:
  
  extension D:B {
    D.y implements B.x
  }
  
All of this is merely to start the discussion, so feel free to propose better syntax or a more elegant solution...

Thoughts?

Thanks,
Jon

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

What about if we synthesised members on the conforming type? So I’m thinking

protocol MyProtoOne {
   func doSomething()
}

protocol MyProtoTwo {
   func doSomething()
}

struct ImplementsOne : MyProtoOne {
   func doSomething() {
     // Unambiguous. Satisfies MyProtoOne.
   }
}

struct ImplementsBoth : MyProtoOne, MyProtoTwo {
   
   func MyProtoOne.doSomething() {
     // Or some other declaration modifier to say this for MyProtoOne
   }

   func MyProtoTwo.doSomething() {
     // ...
   }
}

ImplementsOne().doSomething() // unambiguous intention at call-site
ImplementsBoth().MyProtoOne.doSomething() // intention at call-site must be explicit. What kind of thing do you want to look at ImplementsBoth() as?
ImplementsBoth().MyProtoTwo.doSomething()

I think this would be relatively simple to implement. We just disambiguate at declaration site, and the synthesised members ImplementsBoth().MyProto{One/Two} are the protocol-witness tables to disambiguate at the call-site.

The thing is that we would need to mangle those names differently, and for ABI resilience (the ability to add a protocol with a conflicting name later) that would mean also mangling the protocol name in to the non-conflicting cases (ImplementsOne.MyProtoOne.doSomething). That means if you had ImplementsOne.doSomething in v1 and added the MyProtoOne protocol in v1.1, you would need to add a plain doSomething() method which doesn’t belong to a protocol.

That’s the thing — members would kind of “belong” to the protocol conformance on the type, rather than being plain top-level members on the type itself (although as long as there are no conflicts, you could use them as such and the compiler would know which members you mean). That may be beneficial for mixins.

Karl


(Xiaodi Wu) #8

Hi everyone,

We talked about this before when we were discussing mixins, and there
seemed to be generally positive feelings towards it as a feature for the
future.

It's been some time now since the original discussion, so perhaps you
could refresh our collective memory (or at least, mine): although it
*seems* like this feature might be useful, I can't recall a concrete use
case where I've felt like I needed this feature--do you have some examples?

Ideally, the biggest use is that it helps to (partially) solve the diamond
problem (and similar issues) by forcing/allowing disambiguation when there
are multiple protocols being conformed to. This will become more of an
issue if we allow protocols or extensions to add storage. Your proposed
syntax actually does a better job of it than mine because mine was always
shown as attached to some sort of implementation, whereas yours could
potentially allow access to a default implementation under a new name.

Other than that, it generally allows us to bypass/mitigate conflicts
between protocols. In the current version, you are unable to conform to
both protocols (either because it won’t compile or because you can’t
satisfy the semantics of both protocols) without designing the protocols
together to avoid conflicts. (I have definitely had to go back and
rename/refactor properties on a protocol for this reason… which I couldn’t
have done if I didn’t control both protocols).

I understand something of the difficulty of confronting the diamond
problem. As I wrote above, I'm inclined to believe that this proposed
feature would help solve a real issue. However, the point I'm trying to
make is that, on reflection, I have never actually been hampered by the
lack of this feature, and so I'd like to continue the discussion to get a
fuller sense of just how impactful this proposal would be, both positive
and negative.

It's true, of course, that if you control at least one of two protocols
(you don't need to control both protocols), it is trivially easy to cause
this problem to occur, but as you point out it is also possible to resolve
the problem by re-designing the protocol you control. I'm inclined to think
(without evidence, admittedly) that re-designing to remove the conflict,
where possible, would actually be the superior option in most cases.

My question was: have you actually run into a scenario that necessitates
the feature you propose because you controlled neither conflicting
protocol? I think it would strengthen the proposal greatly to have a
concrete, uncontrived example.

Take a look at Eiffel’s ‘rename’ & ’select’ features for similar

functionality and use-cases.

Ultimately, this is a step in the direction of having true mixins.

Sure, maybe. I couldn't evaluate that claim. I'm inclined to favor the
proposal, but it'd have to stand on its own merits, not as a step to an
as-yet undesigned feature.

   I am fairly certain this affects the ABI though, so I thought I would

bring it up now.

If two protocols have methods/properties with the same name, but
different signatures, we need a way to distinguish between them when
attempting to conform to both.

        protocol A {
                var x:Int {get set}
        }

        protocol B {
                var x:Double {get set}
        }

Methods can be overloaded that differ in arguments or return type, so it
seems like this problem mainly exists with *properties* that differ in
type--am I wrong?

There is also the case of functions with the same name and signature, but
different semantics. There may be no single implementation which
simultaneously satisfies the semantics for both protocols. By renaming one
of the functions, we are able to provide separate implementations for each
requirement (which allows both protocols to function as intended).

True. However, putting on my critical hat, this seems like we're stretching
to provide support for an anti-pattern. It'd be nice to have an example
where one runs into the motivating problem *and* where the proposed feature
promotes a _better_ design than is currently possible, rather than making a
bad design compile.

At this point, I'm imagining scenarios where a user is trying to conform a
type MyAnimal to both Biped and Quadruped, then worrying that `walk()` has
two semantics: something has already gone deeply wrong IMO.

[Yes, I know there are animals that can sometimes walk on two or four legs.
The point here is that the protocols were clearly designed to model animals
at a certain level of detail, while it appears that the user writing
`MyAnimal` wants to model the animal at a different level of detail than
either protocol was designed to handle. You might have specific qualms
about this particular hypothetical, but I think you can pick out the
general point that there is a much larger problem inherent in the design
than the specific problem regarding two colliding method signatures.]

There may also be functions in different protocols with different names but

the same semantics and signature. This will allow a single implementation
to satisfy both protocols without duplication.

This is a poor argument IMO. You can already implement foo() and then have
bar() forward to foo() with trivial effort and really minimal boilerplate.
It's an existing solution, and a more general solution because it doesn't
require matching signatures. Also, it's a better solution IMO because it
preserves the notion that a type T : Fooable, Barrable provides the full
API guaranteed by Fooable and Barrable.

Finally, we may want to rename an inherited default implementation to avoid

conflicting with another protocol's default implementation in cases where
we don’t want to override it.

Yes, I think this would be handy. I can't think of an existing way to do
this, and I expect it might make a big difference in designing good
protocols. So here, I think we have a strong argument.

Again, though, could we find a concrete example of how this feature would
promoter a better design of an actual type and/or protocol?

One possibility is to allow a struct/class/enum to conform to the protocol

while renaming one (or both) of the clashing methods:

        struct C: A,B {
                var x:Int
                var y:Double implements B.x
        }

The conforming method/property would still have to have the same
signature, but could have a different name (and parameter labels). It
would also allow protocol methods which have identical signatures and
semantics, but different names to be implemented using the same method (i.e
‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the
property (e.g. ‘x’) would end up calling the implementation of the renamed
property ( ‘y’ in this example) on the conforming type.

Reflecting on this proposed change, it occurs to me that something of
value would be lost, and I think that this something is actually rather
valuable:

Today, when I see that a type conforms to (for example) Sequence, I know
that certain methods and/or properties exist on that type. Protocol
conformance guarantees a certain API, not just certain semantics.

It isn’t actually lost, however. When working with it as a Sequence (for
example), that API would be intact using the original names. It is only
when working with it as its own type that the renaming would have an effect.

That is not my point. With this proposal, knowing that MyGreatType conforms
to Sequence would no longer yield any information as to the MyGreatType
API. That is definitely something lost and we should acknowledge that.

Also, recall that Sequence has Self or associated type requirements. So:

let m = MyGreatType()
// There is nothing I can write here to use the Sequence API with `m`, IIUC;
// however, depending on how this feature is designed, I *might* be able to
// call a generic function that operates on a type `T : Sequence` and work
// with `m` that way.

Perhaps one way to mitigate this loss would be to have any renamed members

···

On Tue, Aug 23, 2016 at 3:02 AM, Jonathan Hull <jhull@gbis.com> wrote:

On Aug 22, 2016, at 11:32 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Mon, Aug 22, 2016 at 11:59 PM, Jonathan Hull via swift-evolution < > swift-evolution@swift.org> wrote:
listed *in the declaration of conformance*, something like this (with some
additional bikeshedding):

struct MyGreatType : Sequence (count => length) {
  // MyGreatType conforms to Sequence but renames `count` to `length`
}

Yes, putting it in the conformance declaration is a definite possibility
we should consider.

I think we would also want a way to retroactively conform using existing

properties/methods in an extension declaring conformance. Not sure what
the best syntax for that would be. Off the top of my head (though I would
love to have something with less cruft):

        extension D:B {
                @conform(to: B.x, with: D.y)
        }

or maybe just:

        extension D:B {
                D.y implements B.x
        }

If renamed members are declared along with protocol conformance, then the
syntax for retroactive modeling follows naturally:

extension D : B (x => y) { }
// again, the actual notation here is ugly
// but the underlying idea, I think, is worth considering

Yup

One thing I like about this is that it helps to solve the diamond problem.
‘x’ could be a default implementation in B which D does not override. I
think this is an important case which my original proposal didn’t address
fully.

We should keep bikeshedding the syntax though...

Thanks,
Jon


(David Waite) #9

C# does something similar to this - you can implement an interface without having the implementation methods published on the object. You must cast/box to the interface in order to access those methods.

This is used for instance to support both the generic and non-generic interfaces for dictionaries in the same class.

I don’t know if an object should expose either method with the same name as the parent protocols if they conflict - I would imagine someone being burned by getting incorrect behavior, and I’m trying to think if there would be interesting semantics with generic code.

-DW

···

On Aug 22, 2016, at 11:39 PM, Karl via swift-evolution <swift-evolution@swift.org> wrote:

On 23 Aug 2016, at 06:59, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

Hi everyone,

We talked about this before when we were discussing mixins, and there seemed to be generally positive feelings towards it as a feature for the future. I am fairly certain this affects the ABI though, so I thought I would bring it up now.

If two protocols have methods/properties with the same name, but different signatures, we need a way to distinguish between them when attempting to conform to both.

  protocol A {
    var x:Int {get set}
  }

  protocol B {
    var x:Double {get set}
  }

One possibility is to allow a struct/class/enum to conform to the protocol while renaming one (or both) of the clashing methods:

  struct C: A,B {
    var x:Int
    var y:Double implements B.x
  }

The conforming method/property would still have to have the same signature, but could have a different name (and parameter labels). It would also allow protocol methods which have identical signatures and semantics, but different names to be implemented using the same method (i.e ‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the property (e.g. ‘x’) would end up calling the implementation of the renamed property ( ‘y’ in this example) on the conforming type.

I think we would also want a way to retroactively conform using existing properties/methods in an extension declaring conformance. Not sure what the best syntax for that would be. Off the top of my head (though I would love to have something with less cruft):

  extension D:B {
    @conform(to: B.x, with: D.y)
  }

or maybe just:
  
  extension D:B {
    D.y implements B.x
  }
  
All of this is merely to start the discussion, so feel free to propose better syntax or a more elegant solution...

Thoughts?

Thanks,
Jon

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

What about if we synthesised members on the conforming type? So I’m thinking

protocol MyProtoOne {
   func doSomething()
}

protocol MyProtoTwo {
   func doSomething()
}

struct ImplementsOne : MyProtoOne {
   func doSomething() {
     // Unambiguous. Satisfies MyProtoOne.
   }
}

struct ImplementsBoth : MyProtoOne, MyProtoTwo {
   
   func MyProtoOne.doSomething() {
     // Or some other declaration modifier to say this for MyProtoOne
   }

   func MyProtoTwo.doSomething() {
     // ...
   }
}

ImplementsOne().doSomething() // unambiguous intention at call-site
ImplementsBoth().MyProtoOne.doSomething() // intention at call-site must be explicit. What kind of thing do you want to look at ImplementsBoth() as?
ImplementsBoth().MyProtoTwo.doSomething()

I think this would be relatively simple to implement. We just disambiguate at declaration site, and the synthesised members ImplementsBoth().MyProto{One/Two} are the protocol-witness tables to disambiguate at the call-site.

The thing is that we would need to mangle those names differently, and for ABI resilience (the ability to add a protocol with a conflicting name later) that would mean also mangling the protocol name in to the non-conflicting cases (ImplementsOne.MyProtoOne.doSomething). That means if you had ImplementsOne.doSomething in v1 and added the MyProtoOne protocol in v1.1, you would need to add a plain doSomething() method which doesn’t belong to a protocol.

That’s the thing — members would kind of “belong” to the protocol conformance on the type, rather than being plain top-level members on the type itself (although as long as there are no conflicts, you could use them as such and the compiler would know which members you mean). That may be beneficial for mixins.

Karl

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jon Hull) #10

Hi everyone,

We talked about this before when we were discussing mixins, and there seemed to be generally positive feelings towards it as a feature for the future. I am fairly certain this affects the ABI though, so I thought I would bring it up now.

If two protocols have methods/properties with the same name, but different signatures, we need a way to distinguish between them when attempting to conform to both.

  protocol A {
    var x:Int {get set}
  }

  protocol B {
    var x:Double {get set}
  }

I believe that this can happen, and it is unfortunate that Swift has no mechanism for dealing with it today. However, I agree with Xiaodi that your proposal would be much stronger with real-world examples rather than theoretical ones.

Agreed. Unfortunately, everything is NDA’d at the moment.

Also, the problem (not the solution) is so obvious to me that I guess I am having trouble explaining it. I never expected to have to argue that namespace collisions can cause problems… I thought everyone had had that experience (especially coming from ObjC).

I am not tied to this proposal, so much as I wanted to start a discussion exploring the possible solution space. The explicit interface idea is a solid option. I like that you wouldn’t necessarily have to expose a protocol’s members without qualification. I should also be able to effectively rename a member by calling the unexposed protocol implementation from a method with my desired name.

tl;dr: I would like people to provide options/syntax for solving this problem (even if they are a bit wild), so that we can cherry pick the best stuff and put together an elegant solution…

Thanks,
Jon

···

On Aug 24, 2016, at 11:38 AM, Douglas Gregor <dgregor@apple.com> wrote:

On Aug 22, 2016, at 9:59 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

One possibility is to allow a struct/class/enum to conform to the protocol while renaming one (or both) of the clashing methods:

  struct C: A,B {
    var x:Int
    var y:Double implements B.x
  }

The conforming method/property would still have to have the same signature, but could have a different name (and parameter labels). It would also allow protocol methods which have identical signatures and semantics, but different names to be implemented using the same method (i.e ‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the property (e.g. ‘x’) would end up calling the implementation of the renamed property ( ‘y’ in this example) on the conforming type.

Sure. Calling through the protocol type will get whatever method/property satisfied the protocol requirement. Yes, there are limits here due to protocols with associated types and Self requirements, but I fully expect those to go away at some point with generalized existentials.

I think we would also want a way to retroactively conform using existing properties/methods in an extension declaring conformance. Not sure what the best syntax for that would be. Off the top of my head (though I would love to have something with less cruft):

  extension D:B {
    @conform(to: B.x, with: D.y)
  }

or maybe just:
  
  extension D:B {
    D.y implements B.x
  }
  
All of this is merely to start the discussion, so feel free to propose better syntax or a more elegant solution...

C# has a much narrower solution that lets you qualify the method declaration (rather than doing a full rename), e.g.,

  struct C : A {
    var x: Int
    var y: Double
  }

  extension C : B {
    var B.x: Double {
      get { return y }
      set { y = newValue }
    }
  }

They have some examples at:

  https://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx <https://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx>

One would have to figure out what the name-lookup rules are, of course, but this might allow us to solve the problem without introducing a generalized renaming mechanism.

  - Doug


(Karl) #11

+1 to the C# mechanism (I think it’s basically what I proposed earlier). Resolving protocol conformance by name is soooo Objective-C. Swift has a much stronger protocol system, and members which belong to a protocol should **belong** to that protocol (have it mangled in to the name, MyType__MyProtocol__MyMember, etc. As a shorthand, when the corresponding protocol is unambiguous you can declare or use it directly (MyType.MyMember).

I’m very much against renaming. It’s a hack; we want a solution. That applies to all instances of renaming - here, in protocol conformances, and in general with imported types (there was a proposal some months ago which involved renaming members when importing).

The two most important things for programmers are how well they know the language, and how well they know the libraries they are using. Renaming makes the latter basically useless. Somebody doesn’t like the long names in Apple’s SDK, and suddenly UITableView becomes Table or even just an emoji (why not?). The code you know and patterns you expect become a bunch of alien hieroglyphics which looked neat and terse to the person writing it but requires extensive study by anybody else.

Renaming is just bad, bad, bad. I’ll argue strongly against adding any kind of symbol renaming to Swift whatsoever for as long as I’m active in the community (except across language barriers, like with @objc(<name>), because the symbols don’t match up anyway so there’s no prior knowledge to break — in fact it’s usually used to retain older APIs and make transitions *less* breaking).

Karl

···

On 24 Aug 2016, at 20:38, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Aug 22, 2016, at 9:59 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi everyone,

We talked about this before when we were discussing mixins, and there seemed to be generally positive feelings towards it as a feature for the future. I am fairly certain this affects the ABI though, so I thought I would bring it up now.

If two protocols have methods/properties with the same name, but different signatures, we need a way to distinguish between them when attempting to conform to both.

  protocol A {
    var x:Int {get set}
  }

  protocol B {
    var x:Double {get set}
  }

I believe that this can happen, and it is unfortunate that Swift has no mechanism for dealing with it today. However, I agree with Xiaodi that your proposal would be much stronger with real-world examples rather than theoretical ones.

One possibility is to allow a struct/class/enum to conform to the protocol while renaming one (or both) of the clashing methods:

  struct C: A,B {
    var x:Int
    var y:Double implements B.x
  }

The conforming method/property would still have to have the same signature, but could have a different name (and parameter labels). It would also allow protocol methods which have identical signatures and semantics, but different names to be implemented using the same method (i.e ‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the property (e.g. ‘x’) would end up calling the implementation of the renamed property ( ‘y’ in this example) on the conforming type.

Sure. Calling through the protocol type will get whatever method/property satisfied the protocol requirement. Yes, there are limits here due to protocols with associated types and Self requirements, but I fully expect those to go away at some point with generalized existentials.

I think we would also want a way to retroactively conform using existing properties/methods in an extension declaring conformance. Not sure what the best syntax for that would be. Off the top of my head (though I would love to have something with less cruft):

  extension D:B {
    @conform(to: B.x, with: D.y)
  }

or maybe just:
  
  extension D:B {
    D.y implements B.x
  }
  
All of this is merely to start the discussion, so feel free to propose better syntax or a more elegant solution...

C# has a much narrower solution that lets you qualify the method declaration (rather than doing a full rename), e.g.,

  struct C : A {
    var x: Int
    var y: Double
  }

  extension C : B {
    var B.x: Double {
      get { return y }
      set { y = newValue }
    }
  }

They have some examples at:

  https://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx <https://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx>

One would have to figure out what the name-lookup rules are, of course, but this might allow us to solve the problem without introducing a generalized renaming mechanism.

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Callionica (Swift)) #12

C# has this feature (called "explicit interface implementation" or
"explicit interface method implementation"). It's useful for implementing
multiple data-like interfaces that have common names like Name, ID, Title,
and Description. It's useful for keeping public interfaces clean. It's
useful for API evolution: sometimes you need to implement both IVersion1
and IVersion2 interfaces that have (as you would expect) similar method
names and semantics, but differences that still require different
implementations.

There's a stack overflow topic which has various perspectives on it:
http://stackoverflow.com/questions/143405/c-sharp-interfaces-implicit-implementation-versus-explicit-implementation

···

On Tue, Aug 23, 2016 at 8:35 AM, Xiaodi Wu via swift-evolution < swift-evolution@swift.org> wrote:

On Tue, Aug 23, 2016 at 3:02 AM, Jonathan Hull <jhull@gbis.com> wrote:

On Aug 22, 2016, at 11:32 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Aug 22, 2016 at 11:59 PM, Jonathan Hull via swift-evolution < >> swift-evolution@swift.org> wrote:

Hi everyone,

We talked about this before when we were discussing mixins, and there
seemed to be generally positive feelings towards it as a feature for the
future.

It's been some time now since the original discussion, so perhaps you
could refresh our collective memory (or at least, mine): although it
*seems* like this feature might be useful, I can't recall a concrete use
case where I've felt like I needed this feature--do you have some examples?

Ideally, the biggest use is that it helps to (partially) solve the
diamond problem (and similar issues) by forcing/allowing disambiguation
when there are multiple protocols being conformed to. This will become
more of an issue if we allow protocols or extensions to add storage. Your
proposed syntax actually does a better job of it than mine because mine was
always shown as attached to some sort of implementation, whereas yours
could potentially allow access to a default implementation under a new name.

Other than that, it generally allows us to bypass/mitigate conflicts
between protocols. In the current version, you are unable to conform to
both protocols (either because it won’t compile or because you can’t
satisfy the semantics of both protocols) without designing the protocols
together to avoid conflicts. (I have definitely had to go back and
rename/refactor properties on a protocol for this reason… which I couldn’t
have done if I didn’t control both protocols).

I understand something of the difficulty of confronting the diamond
problem. As I wrote above, I'm inclined to believe that this proposed
feature would help solve a real issue. However, the point I'm trying to
make is that, on reflection, I have never actually been hampered by the
lack of this feature, and so I'd like to continue the discussion to get a
fuller sense of just how impactful this proposal would be, both positive
and negative.

It's true, of course, that if you control at least one of two protocols
(you don't need to control both protocols), it is trivially easy to cause
this problem to occur, but as you point out it is also possible to resolve
the problem by re-designing the protocol you control. I'm inclined to think
(without evidence, admittedly) that re-designing to remove the conflict,
where possible, would actually be the superior option in most cases.

My question was: have you actually run into a scenario that necessitates
the feature you propose because you controlled neither conflicting
protocol? I think it would strengthen the proposal greatly to have a
concrete, uncontrived example.

Take a look at Eiffel’s ‘rename’ & ’select’ features for similar

functionality and use-cases.

Ultimately, this is a step in the direction of having true mixins.

Sure, maybe. I couldn't evaluate that claim. I'm inclined to favor the
proposal, but it'd have to stand on its own merits, not as a step to an
as-yet undesigned feature.

   I am fairly certain this affects the ABI though, so I thought I would

bring it up now.

If two protocols have methods/properties with the same name, but
different signatures, we need a way to distinguish between them when
attempting to conform to both.

        protocol A {
                var x:Int {get set}
        }

        protocol B {
                var x:Double {get set}
        }

Methods can be overloaded that differ in arguments or return type, so it
seems like this problem mainly exists with *properties* that differ in
type--am I wrong?

There is also the case of functions with the same name and signature, but
different semantics. There may be no single implementation which
simultaneously satisfies the semantics for both protocols. By renaming one
of the functions, we are able to provide separate implementations for each
requirement (which allows both protocols to function as intended).

True. However, putting on my critical hat, this seems like we're
stretching to provide support for an anti-pattern. It'd be nice to have an
example where one runs into the motivating problem *and* where the proposed
feature promotes a _better_ design than is currently possible, rather than
making a bad design compile.

At this point, I'm imagining scenarios where a user is trying to conform a
type MyAnimal to both Biped and Quadruped, then worrying that `walk()` has
two semantics: something has already gone deeply wrong IMO.

[Yes, I know there are animals that can sometimes walk on two or four
legs. The point here is that the protocols were clearly designed to model
animals at a certain level of detail, while it appears that the user
writing `MyAnimal` wants to model the animal at a different level of detail
than either protocol was designed to handle. You might have specific qualms
about this particular hypothetical, but I think you can pick out the
general point that there is a much larger problem inherent in the design
than the specific problem regarding two colliding method signatures.]

There may also be functions in different protocols with different names

but the same semantics and signature. This will allow a single
implementation to satisfy both protocols without duplication.

This is a poor argument IMO. You can already implement foo() and then have
bar() forward to foo() with trivial effort and really minimal boilerplate.
It's an existing solution, and a more general solution because it doesn't
require matching signatures. Also, it's a better solution IMO because it
preserves the notion that a type T : Fooable, Barrable provides the full
API guaranteed by Fooable and Barrable.

Finally, we may want to rename an inherited default implementation to

avoid conflicting with another protocol's default implementation in cases
where we don’t want to override it.

Yes, I think this would be handy. I can't think of an existing way to do
this, and I expect it might make a big difference in designing good
protocols. So here, I think we have a strong argument.

Again, though, could we find a concrete example of how this feature would
promoter a better design of an actual type and/or protocol?

One possibility is to allow a struct/class/enum to conform to the

protocol while renaming one (or both) of the clashing methods:

        struct C: A,B {
                var x:Int
                var y:Double implements B.x
        }

The conforming method/property would still have to have the same
signature, but could have a different name (and parameter labels). It
would also allow protocol methods which have identical signatures and
semantics, but different names to be implemented using the same method (i.e
‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the
property (e.g. ‘x’) would end up calling the implementation of the renamed
property ( ‘y’ in this example) on the conforming type.

Reflecting on this proposed change, it occurs to me that something of
value would be lost, and I think that this something is actually rather
valuable:

Today, when I see that a type conforms to (for example) Sequence, I know
that certain methods and/or properties exist on that type. Protocol
conformance guarantees a certain API, not just certain semantics.

It isn’t actually lost, however. When working with it as a Sequence (for
example), that API would be intact using the original names. It is only
when working with it as its own type that the renaming would have an effect.

That is not my point. With this proposal, knowing that MyGreatType
conforms to Sequence would no longer yield any information as to the
MyGreatType API. That is definitely something lost and we should
acknowledge that.

Also, recall that Sequence has Self or associated type requirements. So:

let m = MyGreatType()
// There is nothing I can write here to use the Sequence API with `m`,
IIUC;
// however, depending on how this feature is designed, I *might* be able to
// call a generic function that operates on a type `T : Sequence` and work
// with `m` that way.

Perhaps one way to mitigate this loss would be to have any renamed members

listed *in the declaration of conformance*, something like this (with some
additional bikeshedding):

struct MyGreatType : Sequence (count => length) {
  // MyGreatType conforms to Sequence but renames `count` to `length`
}

Yes, putting it in the conformance declaration is a definite possibility
we should consider.

I think we would also want a way to retroactively conform using existing

properties/methods in an extension declaring conformance. Not sure what
the best syntax for that would be. Off the top of my head (though I would
love to have something with less cruft):

        extension D:B {
                @conform(to: B.x, with: D.y)
        }

or maybe just:

        extension D:B {
                D.y implements B.x
        }

If renamed members are declared along with protocol conformance, then the
syntax for retroactive modeling follows naturally:

extension D : B (x => y) { }
// again, the actual notation here is ugly
// but the underlying idea, I think, is worth considering

Yup

One thing I like about this is that it helps to solve the diamond
problem. ‘x’ could be a default implementation in B which D does not
override. I think this is an important case which my original proposal
didn’t address fully.

We should keep bikeshedding the syntax though...

Thanks,
Jon

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jon Hull) #13

Hi everyone,

We talked about this before when we were discussing mixins, and there seemed to be generally positive feelings towards it as a feature for the future.

It's been some time now since the original discussion, so perhaps you could refresh our collective memory (or at least, mine): although it *seems* like this feature might be useful, I can't recall a concrete use case where I've felt like I needed this feature--do you have some examples?

Ideally, the biggest use is that it helps to (partially) solve the diamond problem (and similar issues) by forcing/allowing disambiguation when there are multiple protocols being conformed to. This will become more of an issue if we allow protocols or extensions to add storage. Your proposed syntax actually does a better job of it than mine because mine was always shown as attached to some sort of implementation, whereas yours could potentially allow access to a default implementation under a new name.

Other than that, it generally allows us to bypass/mitigate conflicts between protocols. In the current version, you are unable to conform to both protocols (either because it won’t compile or because you can’t satisfy the semantics of both protocols) without designing the protocols together to avoid conflicts. (I have definitely had to go back and rename/refactor properties on a protocol for this reason… which I couldn’t have done if I didn’t control both protocols).

I understand something of the difficulty of confronting the diamond problem. As I wrote above, I'm inclined to believe that this proposed feature would help solve a real issue. However, the point I'm trying to make is that, on reflection, I have never actually been hampered by the lack of this feature, and so I'd like to continue the discussion to get a fuller sense of just how impactful this proposal would be, both positive and negative.

It's true, of course, that if you control at least one of two protocols (you don't need to control both protocols), it is trivially easy to cause this problem to occur, but as you point out it is also possible to resolve the problem by re-designing the protocol you control. I'm inclined to think (without evidence, admittedly) that re-designing to remove the conflict, where possible, would actually be the superior option in most cases.

My question was: have you actually run into a scenario that necessitates the feature you propose because you controlled neither conflicting protocol? I think it would strengthen the proposal greatly to have a concrete, uncontrived example.

Right now I commonly have to hand-namespace protocol methods/properties to avoid conflicts. So instead of having ‘var image:UIImage’ (which is the name which makes the most sense in the protocol’s context), I have ‘var protocolNameImage:UIImage’. There are lots of things which have common properties like ‘count’ which have to be called ‘somethingCount’ or ‘countOfSomething’. In the context of the protocol, these names are full of redundant words (especially when measured against the new naming guidelines). We are all used to doing this for Objective C, but it feels out of place in Swift.

This will become a much more serious issue as the third-party code ecosystem grows. Without some capability like this, you will have frameworks which can’t be used together (or at least with the same object). I would hate to see a ‘best practice’ emerge of adding 3 letter prefixes to all protocol methods to get around compatibility issues.

Take a look at Eiffel’s ‘rename’ & ’select’ features for similar functionality and use-cases.

Ultimately, this is a step in the direction of having true mixins.

Sure, maybe. I couldn't evaluate that claim. I'm inclined to favor the proposal, but it'd have to stand on its own merits, not as a step to an as-yet undesigned feature.

This would be part of that design. We have to start somewhere. A journey of 1000 miles begins with a single step.

   I am fairly certain this affects the ABI though, so I thought I would bring it up now.

If two protocols have methods/properties with the same name, but different signatures, we need a way to distinguish between them when attempting to conform to both.

        protocol A {
                var x:Int {get set}
        }

        protocol B {
                var x:Double {get set}
        }

Methods can be overloaded that differ in arguments or return type, so it seems like this problem mainly exists with *properties* that differ in type--am I wrong?

There is also the case of functions with the same name and signature, but different semantics. There may be no single implementation which simultaneously satisfies the semantics for both protocols. By renaming one of the functions, we are able to provide separate implementations for each requirement (which allows both protocols to function as intended).

True. However, putting on my critical hat, this seems like we're stretching to provide support for an anti-pattern. It'd be nice to have an example where one runs into the motivating problem *and* where the proposed feature promotes a _better_ design than is currently possible, rather than making a bad design compile.

At this point, I'm imagining scenarios where a user is trying to conform a type MyAnimal to both Biped and Quadruped, then worrying that `walk()` has two semantics: something has already gone deeply wrong IMO.

[Yes, I know there are animals that can sometimes walk on two or four legs. The point here is that the protocols were clearly designed to model animals at a certain level of detail, while it appears that the user writing `MyAnimal` wants to model the animal at a different level of detail than either protocol was designed to handle. You might have specific qualms about this particular hypothetical, but I think you can pick out the general point that there is a much larger problem inherent in the design than the specific problem regarding two colliding method signatures.]

To take your example of walk(). Perhaps we have a protocol ‘Walkable’ which refers to any data structure where the nodes can be walked using the ‘walk()’ function. It is easy to imagine two different protocols A & B which specialize on this in different ways (say LinearWalkable & RandomWalkable), and both add some methods/properties and use those to provide efficient default implementations. At some point, you may run into a data structure which could easily be walked in both ways.

As things are right now, you couldn’t inherit from both protocols. While you could add new ‘linearWalk()’ & ‘randomWalk()’ to the protocols respectively (cluttering their interface), there is still the issue of what to do when 'walk()’ is called. You can’t rename walk() in the originating protocols because it comes from their common ancestor. Much better to force one (or both) of the methods to be renamed on the conforming data structure. That keeps the interfaces of the protocols clean and makes the options available on the data structure clearer (e.g. ‘walk()’ & ‘randomWalk()’ )

What I have had to do in the current version is inherit from the original protocol and then copy and paste the default implementations from the specialized versions. Now my code has been duplicated and is harder to maintain. We can do better.

There may also be functions in different protocols with different names but the same semantics and signature. This will allow a single implementation to satisfy both protocols without duplication.

This is a poor argument IMO. You can already implement foo() and then have bar() forward to foo() with trivial effort and really minimal boilerplate. It's an existing solution, and a more general solution because it doesn't require matching signatures. Also, it's a better solution IMO because it preserves the notion that a type T : Fooable, Barrable provides the full API guaranteed by Fooable and Barrable.

Finally, we may want to rename an inherited default implementation to avoid conflicting with another protocol's default implementation in cases where we don’t want to override it.

Yes, I think this would be handy. I can't think of an existing way to do this, and I expect it might make a big difference in designing good protocols. So here, I think we have a strong argument.

Again, though, could we find a concrete example of how this feature would promoter a better design of an actual type and/or protocol?

One possibility is to allow a struct/class/enum to conform to the protocol while renaming one (or both) of the clashing methods:

        struct C: A,B {
                var x:Int
                var y:Double implements B.x
        }

The conforming method/property would still have to have the same signature, but could have a different name (and parameter labels). It would also allow protocol methods which have identical signatures and semantics, but different names to be implemented using the same method (i.e ‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the property (e.g. ‘x’) would end up calling the implementation of the renamed property ( ‘y’ in this example) on the conforming type.

Reflecting on this proposed change, it occurs to me that something of value would be lost, and I think that this something is actually rather valuable:

Today, when I see that a type conforms to (for example) Sequence, I know that certain methods and/or properties exist on that type. Protocol conformance guarantees a certain API, not just certain semantics.

It isn’t actually lost, however. When working with it as a Sequence (for example), that API would be intact using the original names. It is only when working with it as its own type that the renaming would have an effect.

That is not my point. With this proposal, knowing that MyGreatType conforms to Sequence would no longer yield any information as to the MyGreatType API. That is definitely something lost and we should acknowledge that.

Also, recall that Sequence has Self or associated type requirements. So:

let m = MyGreatType()
// There is nothing I can write here to use the Sequence API with `m`, IIUC;
// however, depending on how this feature is designed, I *might* be able to
// call a generic function that operates on a type `T : Sequence` and work
// with `m` that way.

I get what you are saying, but this is really asking for an Xcode feature to better show the generated interfaces. Code completion will still work properly.

···

On Aug 23, 2016, at 8:35 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Aug 23, 2016 at 3:02 AM, Jonathan Hull <jhull@gbis.com <mailto:jhull@gbis.com>> wrote:

On Aug 22, 2016, at 11:32 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Mon, Aug 22, 2016 at 11:59 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Perhaps one way to mitigate this loss would be to have any renamed members listed *in the declaration of conformance*, something like this (with some additional bikeshedding):

struct MyGreatType : Sequence (count => length) {
  // MyGreatType conforms to Sequence but renames `count` to `length`
}

Yes, putting it in the conformance declaration is a definite possibility we should consider.

I think we would also want a way to retroactively conform using existing properties/methods in an extension declaring conformance. Not sure what the best syntax for that would be. Off the top of my head (though I would love to have something with less cruft):

        extension D:B {
                @conform(to: B.x, with: D.y)
        }

or maybe just:

        extension D:B {
                D.y implements B.x
        }

If renamed members are declared along with protocol conformance, then the syntax for retroactive modeling follows naturally:

extension D : B (x => y) { }
// again, the actual notation here is ugly
// but the underlying idea, I think, is worth considering

Yup

One thing I like about this is that it helps to solve the diamond problem. ‘x’ could be a default implementation in B which D does not override. I think this is an important case which my original proposal didn’t address fully.

We should keep bikeshedding the syntax though...

Thanks,
Jon


(Xiaodi Wu) #14

Hi everyone,

We talked about this before when we were discussing mixins, and there
seemed to be generally positive feelings towards it as a feature for the
future. I am fairly certain this affects the ABI though, so I thought I
would bring it up now.

If two protocols have methods/properties with the same name, but different
signatures, we need a way to distinguish between them when attempting to
conform to both.

protocol A {
var x:Int {get set}
}

protocol B {
var x:Double {get set}
}

I believe that this can happen, and it is unfortunate that Swift has no
mechanism for dealing with it today. However, I agree with Xiaodi that your
proposal would be much stronger with real-world examples rather than
theoretical ones.

One possibility is to allow a struct/class/enum to conform to the protocol
while renaming one (or both) of the clashing methods:

struct C: A,B {
var x:Int
var y:Double implements B.x
}

The conforming method/property would still have to have the same
signature, but could have a different name (and parameter labels). It
would also allow protocol methods which have identical signatures and
semantics, but different names to be implemented using the same method (i.e
‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the
property (e.g. ‘x’) would end up calling the implementation of the renamed
property ( ‘y’ in this example) on the conforming type.

Sure. Calling through the protocol type will get whatever method/property
satisfied the protocol requirement. Yes, there are limits here due to
protocols with associated types and Self requirements, but I fully expect
those to go away at some point with generalized existentials.

I think we would also want a way to retroactively conform using existing
properties/methods in an extension declaring conformance. Not sure what
the best syntax for that would be. Off the top of my head (though I would
love to have something with less cruft):

extension D:B {
@conform(to: B.x, with: D.y)
}

or maybe just:

extension D:B {
D.y implements B.x
}

All of this is merely to start the discussion, so feel free to propose
better syntax or a more elegant solution...

C# has a much narrower solution that lets you qualify the method
declaration (rather than doing a full rename), e.g.,

struct C : A {
  var x: Int
  var y: Double
}

extension C : B {
  var B.x: Double {
    get { return y }
    set { y = newValue }
  }
}

They have some examples at:

https://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx

One would have to figure out what the name-lookup rules are, of course,
but this might allow us to solve the problem without introducing a
generalized renaming mechanism.

- Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

+1 to the C# mechanism (I think it’s basically what I proposed earlier).
Resolving protocol conformance by name is soooo Objective-C. Swift has a
much stronger protocol system, and members which belong to a protocol
should **belong** to that protocol (have it mangled in to the name,
MyType__MyProtocol__MyMember, etc. As a shorthand, when the corresponding
protocol is unambiguous you can declare or use it directly
(MyType.MyMember).

I’m very much against renaming. It’s a hack; we want a solution. That
applies to all instances of renaming - here, in protocol conformances, and
in general with imported types (there was a proposal some months ago which
involved renaming members when importing).

The two most important things for programmers are how well they know the
language, and how well they know the libraries they are using. Renaming
makes the latter basically useless. Somebody doesn’t like the long names in
Apple’s SDK, and suddenly UITableView becomes Table or even just an emoji
(why not?). The code you know and patterns you expect become a bunch of
alien hieroglyphics which looked neat and terse to the person writing it
but requires extensive study by anybody else.

Renaming is just bad, bad, bad. I’ll argue strongly against adding any
kind of symbol renaming to Swift whatsoever for as long as I’m active in
the community (except across language barriers, like with @objc(<name>),
because the symbols don’t match up anyway so there’s no prior knowledge to
break — in fact it’s usually used to retain older APIs and make transitions
*less* breaking).

After some reflection, I think I largely agree with Karl on this point. A
C#-like explicit implementation mechanism might be the way to go; it solves
the issue of colliding names without introducing the difficulties that
arise from arbitrary renaming.

Where I would disagree with Karl would be on mangling the protocol names
into member names; as I wrote above, I strongly hold to the view that it's
important that protocols guarantee the API of conforming types, so I think
that protocol requirements should be in the same namespace as all other
type members *unless* something else is explicitly necessary due to a
collision. However, in the end, the disagreement here is more conceptual
than practical, as everyday use of the feature would be largely similar in
any case.

Based on that view, however, I'd go a step further in
simplifying/rationalizing the C#-like mechanism, like so:

* I'd propose that conformance to a protocol can be either as it is in
Swift 3 (let's call it "direct") or it can be what C# would call explicit
(let's call it "indirect", just for good measure); unlike C#, this choice
would refer to conformance as a whole, not to implementation of specific
members (although this is more of a notional difference from C#, since as
proposed below, specific members may or may not be accessible directly
without casting).

* Members declared directly on the type that match the requirements of an
"indirectly conformed" protocol will satisfy those requirements, but any
remaining requirements not satisfied in that manner (for instance, due to
colliding names) must be implemented in an extension that explicitly
declares indirect conformance. For example:

protocol A {
  var x: Int { get }
  var y: Int { get }
}

protocol B {
  var x: Double { get }
  var y: Int { get }
}

struct S : A {
  var x: Int { return 42 }
  var y: Int { return 43 }
  // we cannot have S conform to B just yet
  // because we can't re-declare `x` as a property of type Double
}

extension S : indirect B {
  var x: Double { return -42 }
  // note that `y` is satisfied by the implementation above
}

* Members declared in an extension adding an "indirect" conformance to a
protocol can only be accessed after the type is cast to the corresponding
existential type. In the example above, `S.x` is an Int (and is the same as
`(S as A).x`) and has value 42, but `(S as B).x` is a Double and has value
-42.0. Meanwhile, `S.y` can be accessed directly or indirectly as either
`(S as A).y` or `(S as B).y` and has value 43.

* Until generics are completed, only protocols without Self or associated
type requirements can be used as an existential, so for now let's limit
indirect conformances to protocols without Self or associated type
requirements; this limit should be relaxed in tandem with the
implementation of generalized existentials.

Would this simplified scheme go far enough in addressing the original issue?

Karl

···

On Wed, Aug 24, 2016 at 3:28 PM, Karl via swift-evolution < swift-evolution@swift.org> wrote:

On 24 Aug 2016, at 20:38, Douglas Gregor via swift-evolution < > swift-evolution@swift.org> wrote:
On Aug 22, 2016, at 9:59 PM, Jonathan Hull via swift-evolution < > swift-evolution@swift.org> wrote:

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #15

C# has this feature (called "explicit interface implementation" or
"explicit interface method implementation"). It's useful for implementing
multiple data-like interfaces that have common names like Name, ID, Title,
and Description. It's useful for keeping public interfaces clean. It's
useful for API evolution: sometimes you need to implement both IVersion1
and IVersion2 interfaces that have (as you would expect) similar method
names and semantics, but differences that still require different
implementations.

There's a stack overflow topic which has various perspectives on it:
http://stackoverflow.com/questions/143405/c-sharp-interfaces-implicit-
implementation-versus-explicit-implementation

This is very interesting information. The OP there, dating from '08,
states: "Microsoft's official guidelines (from first edition Framework
Design Guidelines) states that using explicit implementations are not
recommended." Is that, to your knowledge, still the consensus opinion from
Microsoft? If the language designers of C# were to do it all over again,
would this feature have been added at all?

Two notable considerations. In this proposal, Jonathan is suggesting that
we _rename_ protocol requirements; I don't think C# EIMI has that feature,
so it would remain to be determined whether this new twist could be an
improvement or a regression as compared to EIMI.

Second, invoking an EIMI member requires creating an interface instance. In
Swift, however, protocols that have Self or associated type requirements
can only be used as generic constraints. It is unclear to me if or how the
Swift type system would represent something analogous to an interface
instance.

···

On Tue, Aug 23, 2016 at 5:51 PM, Callionica (Swift) < swift-callionica@callionica.com> wrote:

On Tue, Aug 23, 2016 at 8:35 AM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

On Tue, Aug 23, 2016 at 3:02 AM, Jonathan Hull <jhull@gbis.com> wrote:

On Aug 22, 2016, at 11:32 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Aug 22, 2016 at 11:59 PM, Jonathan Hull via swift-evolution < >>> swift-evolution@swift.org> wrote:

Hi everyone,

We talked about this before when we were discussing mixins, and there
seemed to be generally positive feelings towards it as a feature for the
future.

It's been some time now since the original discussion, so perhaps you
could refresh our collective memory (or at least, mine): although it
*seems* like this feature might be useful, I can't recall a concrete use
case where I've felt like I needed this feature--do you have some examples?

Ideally, the biggest use is that it helps to (partially) solve the
diamond problem (and similar issues) by forcing/allowing disambiguation
when there are multiple protocols being conformed to. This will become
more of an issue if we allow protocols or extensions to add storage. Your
proposed syntax actually does a better job of it than mine because mine was
always shown as attached to some sort of implementation, whereas yours
could potentially allow access to a default implementation under a new name.

Other than that, it generally allows us to bypass/mitigate conflicts
between protocols. In the current version, you are unable to conform to
both protocols (either because it won’t compile or because you can’t
satisfy the semantics of both protocols) without designing the protocols
together to avoid conflicts. (I have definitely had to go back and
rename/refactor properties on a protocol for this reason… which I couldn’t
have done if I didn’t control both protocols).

I understand something of the difficulty of confronting the diamond
problem. As I wrote above, I'm inclined to believe that this proposed
feature would help solve a real issue. However, the point I'm trying to
make is that, on reflection, I have never actually been hampered by the
lack of this feature, and so I'd like to continue the discussion to get a
fuller sense of just how impactful this proposal would be, both positive
and negative.

It's true, of course, that if you control at least one of two protocols
(you don't need to control both protocols), it is trivially easy to cause
this problem to occur, but as you point out it is also possible to resolve
the problem by re-designing the protocol you control. I'm inclined to think
(without evidence, admittedly) that re-designing to remove the conflict,
where possible, would actually be the superior option in most cases.

My question was: have you actually run into a scenario that necessitates
the feature you propose because you controlled neither conflicting
protocol? I think it would strengthen the proposal greatly to have a
concrete, uncontrived example.

Take a look at Eiffel’s ‘rename’ & ’select’ features for similar

functionality and use-cases.

Ultimately, this is a step in the direction of having true mixins.

Sure, maybe. I couldn't evaluate that claim. I'm inclined to favor the
proposal, but it'd have to stand on its own merits, not as a step to an
as-yet undesigned feature.

   I am fairly certain this affects the ABI though, so I thought I would

bring it up now.

If two protocols have methods/properties with the same name, but
different signatures, we need a way to distinguish between them when
attempting to conform to both.

        protocol A {
                var x:Int {get set}
        }

        protocol B {
                var x:Double {get set}
        }

Methods can be overloaded that differ in arguments or return type, so it
seems like this problem mainly exists with *properties* that differ in
type--am I wrong?

There is also the case of functions with the same name and signature,
but different semantics. There may be no single implementation which
simultaneously satisfies the semantics for both protocols. By renaming one
of the functions, we are able to provide separate implementations for each
requirement (which allows both protocols to function as intended).

True. However, putting on my critical hat, this seems like we're
stretching to provide support for an anti-pattern. It'd be nice to have an
example where one runs into the motivating problem *and* where the proposed
feature promotes a _better_ design than is currently possible, rather than
making a bad design compile.

At this point, I'm imagining scenarios where a user is trying to conform
a type MyAnimal to both Biped and Quadruped, then worrying that `walk()`
has two semantics: something has already gone deeply wrong IMO.

[Yes, I know there are animals that can sometimes walk on two or four
legs. The point here is that the protocols were clearly designed to model
animals at a certain level of detail, while it appears that the user
writing `MyAnimal` wants to model the animal at a different level of detail
than either protocol was designed to handle. You might have specific qualms
about this particular hypothetical, but I think you can pick out the
general point that there is a much larger problem inherent in the design
than the specific problem regarding two colliding method signatures.]

There may also be functions in different protocols with different names

but the same semantics and signature. This will allow a single
implementation to satisfy both protocols without duplication.

This is a poor argument IMO. You can already implement foo() and then
have bar() forward to foo() with trivial effort and really minimal
boilerplate. It's an existing solution, and a more general solution because
it doesn't require matching signatures. Also, it's a better solution IMO
because it preserves the notion that a type T : Fooable, Barrable provides
the full API guaranteed by Fooable and Barrable.

Finally, we may want to rename an inherited default implementation to

avoid conflicting with another protocol's default implementation in cases
where we don’t want to override it.

Yes, I think this would be handy. I can't think of an existing way to do
this, and I expect it might make a big difference in designing good
protocols. So here, I think we have a strong argument.

Again, though, could we find a concrete example of how this feature would
promoter a better design of an actual type and/or protocol?

One possibility is to allow a struct/class/enum to conform to the

protocol while renaming one (or both) of the clashing methods:

        struct C: A,B {
                var x:Int
                var y:Double implements B.x
        }

The conforming method/property would still have to have the same
signature, but could have a different name (and parameter labels). It
would also allow protocol methods which have identical signatures and
semantics, but different names to be implemented using the same method (i.e
‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the
property (e.g. ‘x’) would end up calling the implementation of the renamed
property ( ‘y’ in this example) on the conforming type.

Reflecting on this proposed change, it occurs to me that something of
value would be lost, and I think that this something is actually rather
valuable:

Today, when I see that a type conforms to (for example) Sequence, I know
that certain methods and/or properties exist on that type. Protocol
conformance guarantees a certain API, not just certain semantics.

It isn’t actually lost, however. When working with it as a Sequence
(for example), that API would be intact using the original names. It is
only when working with it as its own type that the renaming would have an
effect.

That is not my point. With this proposal, knowing that MyGreatType
conforms to Sequence would no longer yield any information as to the
MyGreatType API. That is definitely something lost and we should
acknowledge that.

Also, recall that Sequence has Self or associated type requirements. So:

let m = MyGreatType()
// There is nothing I can write here to use the Sequence API with `m`,
IIUC;
// however, depending on how this feature is designed, I *might* be able
to
// call a generic function that operates on a type `T : Sequence` and work
// with `m` that way.

Perhaps one way to mitigate this loss would be to have any renamed

members listed *in the declaration of conformance*, something like this
(with some additional bikeshedding):

struct MyGreatType : Sequence (count => length) {
  // MyGreatType conforms to Sequence but renames `count` to `length`
}

Yes, putting it in the conformance declaration is a definite possibility
we should consider.

I think we would also want a way to retroactively conform using existing

properties/methods in an extension declaring conformance. Not sure what
the best syntax for that would be. Off the top of my head (though I would
love to have something with less cruft):

        extension D:B {
                @conform(to: B.x, with: D.y)
        }

or maybe just:

        extension D:B {
                D.y implements B.x
        }

If renamed members are declared along with protocol conformance, then
the syntax for retroactive modeling follows naturally:

extension D : B (x => y) { }
// again, the actual notation here is ugly
// but the underlying idea, I think, is worth considering

Yup

One thing I like about this is that it helps to solve the diamond
problem. ‘x’ could be a default implementation in B which D does not
override. I think this is an important case which my original proposal
didn’t address fully.

We should keep bikeshedding the syntax though...

Thanks,
Jon

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Charles Srstka) #16

I had a very similar situation, in fact. I solved it like this (using your protocol names for the purpose of making it easily contrastable):

protocol Walkable {
  func walk()
}

protocol LinearWalkable: Walkable {
  func linearWalk()
}

extension LinearWalkable {
  func linearWalk { … }

  func walk() { self.linearWalk() }
}

protocol RandomWalkable {
  func randomWalk()
}

extension RandomWalkable {
  func randomWalk() { … }

  func walk() { self.randomWalk() }
}

struct S: LinearWalkable, RandomWalkable {
  func walk() { self.linearWalk() }
}

Thus a type that implements one or the other of the protocols gets walk() automatically doing the right thing, and a type that implements both has to explicitly choose. This way at least avoids the duplication of code; however, it is quite ugly to have to systematically duplicate essentially every method in the protocols (and there turned out to be quite a few of these).

Charles

···

On Aug 24, 2016, at 3:39 AM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

To take your example of walk(). Perhaps we have a protocol ‘Walkable’ which refers to any data structure where the nodes can be walked using the ‘walk()’ function. It is easy to imagine two different protocols A & B which specialize on this in different ways (say LinearWalkable & RandomWalkable), and both add some methods/properties and use those to provide efficient default implementations. At some point, you may run into a data structure which could easily be walked in both ways.

As things are right now, you couldn’t inherit from both protocols. While you could add new ‘linearWalk()’ & ‘randomWalk()’ to the protocols respectively (cluttering their interface), there is still the issue of what to do when 'walk()’ is called. You can’t rename walk() in the originating protocols because it comes from their common ancestor. Much better to force one (or both) of the methods to be renamed on the conforming data structure. That keeps the interfaces of the protocols clean and makes the options available on the data structure clearer (e.g. ‘walk()’ & ‘randomWalk()’ )

What I have had to do in the current version is inherit from the original protocol and then copy and paste the default implementations from the specialized versions. Now my code has been duplicated and is harder to maintain. We can do better.


(Xiaodi Wu) #17

Hi everyone,

We talked about this before when we were discussing mixins, and there
seemed to be generally positive feelings towards it as a feature for the
future.

It's been some time now since the original discussion, so perhaps you
could refresh our collective memory (or at least, mine): although it
*seems* like this feature might be useful, I can't recall a concrete use
case where I've felt like I needed this feature--do you have some examples?

Ideally, the biggest use is that it helps to (partially) solve the
diamond problem (and similar issues) by forcing/allowing disambiguation
when there are multiple protocols being conformed to. This will become
more of an issue if we allow protocols or extensions to add storage. Your
proposed syntax actually does a better job of it than mine because mine was
always shown as attached to some sort of implementation, whereas yours
could potentially allow access to a default implementation under a new name.

Other than that, it generally allows us to bypass/mitigate conflicts
between protocols. In the current version, you are unable to conform to
both protocols (either because it won’t compile or because you can’t
satisfy the semantics of both protocols) without designing the protocols
together to avoid conflicts. (I have definitely had to go back and
rename/refactor properties on a protocol for this reason… which I couldn’t
have done if I didn’t control both protocols).

I understand something of the difficulty of confronting the diamond
problem. As I wrote above, I'm inclined to believe that this proposed
feature would help solve a real issue. However, the point I'm trying to
make is that, on reflection, I have never actually been hampered by the
lack of this feature, and so I'd like to continue the discussion to get a
fuller sense of just how impactful this proposal would be, both positive
and negative.

It's true, of course, that if you control at least one of two protocols
(you don't need to control both protocols), it is trivially easy to cause
this problem to occur, but as you point out it is also possible to resolve
the problem by re-designing the protocol you control. I'm inclined to think
(without evidence, admittedly) that re-designing to remove the conflict,
where possible, would actually be the superior option in most cases.

My question was: have you actually run into a scenario that necessitates
the feature you propose because you controlled neither conflicting
protocol? I think it would strengthen the proposal greatly to have a
concrete, uncontrived example.

Right now I commonly have to hand-namespace protocol methods/properties to
avoid conflicts. So instead of having ‘var image:UIImage’ (which is the
name which makes the most sense in the protocol’s context), I have ‘var
protocolNameImage:UIImage’. There are lots of things which have common
properties like ‘count’ which have to be called ‘somethingCount’ or
‘countOfSomething’. In the context of the protocol, these names are full
of redundant words (especially when measured against the new naming
guidelines). We are all used to doing this for Objective C, but it feels
out of place in Swift.

This will become a much more serious issue as the third-party code
ecosystem grows. Without some capability like this, you will have
frameworks which can’t be used together (or at least with the same
object). I would hate to see a ‘best practice’ emerge of adding 3 letter
prefixes to all protocol methods to get around compatibility issues.

Ah, well this isn't exactly the diamond problem you're talking about here.
Instead, I think, we have a fundamental disagreement. I think I've been
told that this opinion of mine is 'insane'--but I hold to it:

Protocols are not merely a vehicle for delivering a reusable bag of code.
One of its most essential purposes is to constrain the shape or API of its
conforming types. Therefore, it is a feature, not a bug, that with every
choice of name in a protocol you foreclose the possibility of composing
that protocol with others that might have colliding names.

Currently, if you the protocol vendor have made the decision that `image`
"makes the most sense in the protocol's context", you must have considered
whether it would be absurd for a conforming type to have another use for
`image`. If it would be absurd, then `image` is the appropriate name for
your protocol requirement and any other word would truly be redundant. But,
if this is only one of many plausible images, then `somethingImage` or
`imageOfSomething` *is* the appropriate name, and trying to shorten the
name isn't at all consistent with Swift guidelines but rather an incorrect
attempt to prioritize brevity over clarity.

What you're arguing is that protocol designers should be able to design
protocols without regard for how they will compose with others in
conforming types, relying on a new member-renaming feature instead. But, as
you point out, you can already use a protocol as a mere bag of code by
naming all members with unique, prefixed names, then have conforming types
forward their own choice of names to these.

This member-renaming feature you propose would enhance the aesthetic
pleasure of the protocol designer, allowing simple names that don't ever
have to appear in the public API of a concrete type to be used for a
protocol member without placing any restrictions on the API of conforming
types. However, I don't see anything wrong with the current hand-prefixing
method being enshrined as "best practice" for the bag-of-code approach to
protocols. If, as you predict, a growing third-party code ecosystem makes
name collisions worse, then in fact having uniquely distinguishable
prefixed members would be less confusing than having conforming types
renaming protocol members as a matter of course.

Take a look at Eiffel’s ‘rename’ & ’select’ features for similar

functionality and use-cases.

Ultimately, this is a step in the direction of having true mixins.

Sure, maybe. I couldn't evaluate that claim. I'm inclined to favor the
proposal, but it'd have to stand on its own merits, not as a step to an
as-yet undesigned feature.

This would be part of that design. We have to start somewhere. A journey
of 1000 miles begins with a single step.

   I am fairly certain this affects the ABI though, so I thought I would

bring it up now.

If two protocols have methods/properties with the same name, but
different signatures, we need a way to distinguish between them when
attempting to conform to both.

        protocol A {
                var x:Int {get set}
        }

        protocol B {
                var x:Double {get set}
        }

Methods can be overloaded that differ in arguments or return type, so it
seems like this problem mainly exists with *properties* that differ in
type--am I wrong?

There is also the case of functions with the same name and signature, but
different semantics. There may be no single implementation which
simultaneously satisfies the semantics for both protocols. By renaming one
of the functions, we are able to provide separate implementations for each
requirement (which allows both protocols to function as intended).

True. However, putting on my critical hat, this seems like we're
stretching to provide support for an anti-pattern. It'd be nice to have an
example where one runs into the motivating problem *and* where the proposed
feature promotes a _better_ design than is currently possible, rather than
making a bad design compile.

At this point, I'm imagining scenarios where a user is trying to conform a
type MyAnimal to both Biped and Quadruped, then worrying that `walk()` has
two semantics: something has already gone deeply wrong IMO.

[Yes, I know there are animals that can sometimes walk on two or four
legs. The point here is that the protocols were clearly designed to model
animals at a certain level of detail, while it appears that the user
writing `MyAnimal` wants to model the animal at a different level of detail
than either protocol was designed to handle. You might have specific qualms
about this particular hypothetical, but I think you can pick out the
general point that there is a much larger problem inherent in the design
than the specific problem regarding two colliding method signatures.]

To take your example of walk(). Perhaps we have a protocol ‘Walkable’
which refers to any data structure where the nodes can be walked using the
‘walk()’ function. It is easy to imagine two different protocols A & B
which specialize on this in different ways (say LinearWalkable &
RandomWalkable), and both add some methods/properties and use those to
provide efficient default implementations. At some point, you may run into
a data structure which could easily be walked in both ways.

As things are right now, you couldn’t inherit from both protocols. While
you could add new ‘linearWalk()’ & ‘randomWalk()’ to the protocols
respectively (cluttering their interface), there is still the issue of what
to do when 'walk()’ is called. You can’t rename walk() in the originating
protocols because it comes from their common ancestor. Much better to
force one (or both) of the methods to be renamed on the conforming data
structure. That keeps the interfaces of the protocols clean and makes the
options available on the data structure clearer (e.g. ‘walk()’ &
‘randomWalk()’ )

What I have had to do in the current version is inherit from the original
protocol and then copy and paste the default implementations from the
specialized versions. Now my code has been duplicated and is harder to
maintain. We can do better.

I think Charles's solution is pretty nice, but he's right that the API
surface area will have to grow. I don't know his original use case, so I
don't know how ugly I'd find the final solution to be in that scenario. In
this particular example, I'd say that having `linearWalk()` and
`randomWalk()` distinguished seems pretty sensible and an overall win for
clarity. If the same vendor controlled all three protocols, then `Walkable`
could have the `walk()` requirement removed altogether for even more
clarity.

Also, just a hunch, but I suspect your hypothetical would never hold. Could
you envision how the requirements for RandomWalkable might be such that
it's possible to implement an efficient _default_ implementation of a
random walk for any of several conforming data structures, but only one of
these data structures is LinearWalkable, _and_ such a linear walk is
efficient using another _default_implementation for an overlapping but not
identical set of data structures? It's not mere trivia here, because the
crux of your argument is that there exist default implementations that
require copying and pasting into conforming types (and sufficiently
efficient default implementations so that copying and pasting is
appropriate rather than implementing a more efficient version). More likely
in diamond problem scenarios, I think, colliding members are going to be
properties or methods either without default implementations or than need
to supply more efficient versions of default implementations anyway.

There may also be functions in different protocols with different names but

···

On Wed, Aug 24, 2016 at 3:39 AM, Jonathan Hull <jhull@gbis.com> wrote:

On Aug 23, 2016, at 8:35 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Aug 23, 2016 at 3:02 AM, Jonathan Hull <jhull@gbis.com> wrote:

On Aug 22, 2016, at 11:32 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Mon, Aug 22, 2016 at 11:59 PM, Jonathan Hull via swift-evolution < >> swift-evolution@swift.org> wrote:
the same semantics and signature. This will allow a single implementation
to satisfy both protocols without duplication.

This is a poor argument IMO. You can already implement foo() and then have
bar() forward to foo() with trivial effort and really minimal boilerplate.
It's an existing solution, and a more general solution because it doesn't
require matching signatures. Also, it's a better solution IMO because it
preserves the notion that a type T : Fooable, Barrable provides the full
API guaranteed by Fooable and Barrable.

Finally, we may want to rename an inherited default implementation to

avoid conflicting with another protocol's default implementation in cases
where we don’t want to override it.

Yes, I think this would be handy. I can't think of an existing way to do
this, and I expect it might make a big difference in designing good
protocols. So here, I think we have a strong argument.

Again, though, could we find a concrete example of how this feature would
promoter a better design of an actual type and/or protocol?

One possibility is to allow a struct/class/enum to conform to the

protocol while renaming one (or both) of the clashing methods:

        struct C: A,B {
                var x:Int
                var y:Double implements B.x
        }

The conforming method/property would still have to have the same
signature, but could have a different name (and parameter labels). It
would also allow protocol methods which have identical signatures and
semantics, but different names to be implemented using the same method (i.e
‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the
property (e.g. ‘x’) would end up calling the implementation of the renamed
property ( ‘y’ in this example) on the conforming type.

Reflecting on this proposed change, it occurs to me that something of
value would be lost, and I think that this something is actually rather
valuable:

Today, when I see that a type conforms to (for example) Sequence, I know
that certain methods and/or properties exist on that type. Protocol
conformance guarantees a certain API, not just certain semantics.

It isn’t actually lost, however. When working with it as a Sequence (for
example), that API would be intact using the original names. It is only
when working with it as its own type that the renaming would have an effect.

That is not my point. With this proposal, knowing that MyGreatType
conforms to Sequence would no longer yield any information as to the
MyGreatType API. That is definitely something lost and we should
acknowledge that.

Also, recall that Sequence has Self or associated type requirements. So:

let m = MyGreatType()
// There is nothing I can write here to use the Sequence API with `m`,
IIUC;
// however, depending on how this feature is designed, I *might* be able to
// call a generic function that operates on a type `T : Sequence` and work
// with `m` that way.

I get what you are saying, but this is really asking for an Xcode feature
to better show the generated interfaces. Code completion will still work
properly.

Perhaps one way to mitigate this loss would be to have any renamed members

listed *in the declaration of conformance*, something like this (with some
additional bikeshedding):

struct MyGreatType : Sequence (count => length) {
  // MyGreatType conforms to Sequence but renames `count` to `length`
}

Yes, putting it in the conformance declaration is a definite possibility
we should consider.

I think we would also want a way to retroactively conform using existing

properties/methods in an extension declaring conformance. Not sure what
the best syntax for that would be. Off the top of my head (though I would
love to have something with less cruft):

        extension D:B {
                @conform(to: B.x, with: D.y)
        }

or maybe just:

        extension D:B {
                D.y implements B.x
        }

If renamed members are declared along with protocol conformance, then the
syntax for retroactive modeling follows naturally:

extension D : B (x => y) { }
// again, the actual notation here is ugly
// but the underlying idea, I think, is worth considering

Yup

One thing I like about this is that it helps to solve the diamond
problem. ‘x’ could be a default implementation in B which D does not
override. I think this is an important case which my original proposal
didn’t address fully.

We should keep bikeshedding the syntax though...

Thanks,
Jon


(Callionica (Swift)) #18

The framework design guidelines were for ensuring code consistency and
necessarily included stylistic decisions. There was no ban on using
explicit interface implementation, just reasonable caution to the end goal
of consistency. This is what the framework design guidelines said in 2010:
https://msdn.microsoft.com/en-us/library/ms229034(v=vs.100).aspx
A web search suggests that the current version of the guidelines contains
no guidance on explicit interface implementation, but could be that I just
couldn't find it.
Explicit implementations were essential for moving collection interfaces
and others to generic versions - there's plenty of uses in the Microsoft
.Net framework implementation - so I would be surprised if there was a
feeling that the feature was a mistake, but you'd have to ask Anders and
others. (I don't know if TypeScript has a similar feature, but if not,
there are alternative explanations for it's absence than thinking it was a
mistake for C#).

You're correct that C# doesn't allow renaming: it requires the interface
name to be prefixed to the method name at the point of definition.

···

On Tue, Aug 23, 2016 at 4:03 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Tue, Aug 23, 2016 at 5:51 PM, Callionica (Swift) < > swift-callionica@callionica.com> wrote:

C# has this feature (called "explicit interface implementation" or
"explicit interface method implementation"). It's useful for implementing
multiple data-like interfaces that have common names like Name, ID, Title,
and Description. It's useful for keeping public interfaces clean. It's
useful for API evolution: sometimes you need to implement both IVersion1
and IVersion2 interfaces that have (as you would expect) similar method
names and semantics, but differences that still require different
implementations.

There's a stack overflow topic which has various perspectives on it:
http://stackoverflow.com/questions/143405/c-sharp-interf
aces-implicit-implementation-versus-explicit-implementation

This is very interesting information. The OP there, dating from '08,
states: "Microsoft's official guidelines (from first edition Framework
Design Guidelines) states that using explicit implementations are not
recommended." Is that, to your knowledge, still the consensus opinion from
Microsoft? If the language designers of C# were to do it all over again,
would this feature have been added at all?

Two notable considerations. In this proposal, Jonathan is suggesting that
we _rename_ protocol requirements; I don't think C# EIMI has that feature,
so it would remain to be determined whether this new twist could be an
improvement or a regression as compared to EIMI.

Second, invoking an EIMI member requires creating an interface instance.
In Swift, however, protocols that have Self or associated type requirements
can only be used as generic constraints. It is unclear to me if or how the
Swift type system would represent something analogous to an interface
instance.

On Tue, Aug 23, 2016 at 8:35 AM, Xiaodi Wu via swift-evolution < >> swift-evolution@swift.org> wrote:

On Tue, Aug 23, 2016 at 3:02 AM, Jonathan Hull <jhull@gbis.com> wrote:

On Aug 22, 2016, at 11:32 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Aug 22, 2016 at 11:59 PM, Jonathan Hull via swift-evolution < >>>> swift-evolution@swift.org> wrote:

Hi everyone,

We talked about this before when we were discussing mixins, and there
seemed to be generally positive feelings towards it as a feature for the
future.

It's been some time now since the original discussion, so perhaps you
could refresh our collective memory (or at least, mine): although it
*seems* like this feature might be useful, I can't recall a concrete use
case where I've felt like I needed this feature--do you have some examples?

Ideally, the biggest use is that it helps to (partially) solve the
diamond problem (and similar issues) by forcing/allowing disambiguation
when there are multiple protocols being conformed to. This will become
more of an issue if we allow protocols or extensions to add storage. Your
proposed syntax actually does a better job of it than mine because mine was
always shown as attached to some sort of implementation, whereas yours
could potentially allow access to a default implementation under a new name.

Other than that, it generally allows us to bypass/mitigate conflicts
between protocols. In the current version, you are unable to conform to
both protocols (either because it won’t compile or because you can’t
satisfy the semantics of both protocols) without designing the protocols
together to avoid conflicts. (I have definitely had to go back and
rename/refactor properties on a protocol for this reason… which I couldn’t
have done if I didn’t control both protocols).

I understand something of the difficulty of confronting the diamond
problem. As I wrote above, I'm inclined to believe that this proposed
feature would help solve a real issue. However, the point I'm trying to
make is that, on reflection, I have never actually been hampered by the
lack of this feature, and so I'd like to continue the discussion to get a
fuller sense of just how impactful this proposal would be, both positive
and negative.

It's true, of course, that if you control at least one of two protocols
(you don't need to control both protocols), it is trivially easy to cause
this problem to occur, but as you point out it is also possible to resolve
the problem by re-designing the protocol you control. I'm inclined to think
(without evidence, admittedly) that re-designing to remove the conflict,
where possible, would actually be the superior option in most cases.

My question was: have you actually run into a scenario that necessitates
the feature you propose because you controlled neither conflicting
protocol? I think it would strengthen the proposal greatly to have a
concrete, uncontrived example.

Take a look at Eiffel’s ‘rename’ & ’select’ features for similar

functionality and use-cases.

Ultimately, this is a step in the direction of having true mixins.

Sure, maybe. I couldn't evaluate that claim. I'm inclined to favor the
proposal, but it'd have to stand on its own merits, not as a step to an
as-yet undesigned feature.

   I am fairly certain this affects the ABI though, so I thought I would

bring it up now.

If two protocols have methods/properties with the same name, but
different signatures, we need a way to distinguish between them when
attempting to conform to both.

        protocol A {
                var x:Int {get set}
        }

        protocol B {
                var x:Double {get set}
        }

Methods can be overloaded that differ in arguments or return type, so
it seems like this problem mainly exists with *properties* that differ in
type--am I wrong?

There is also the case of functions with the same name and signature,
but different semantics. There may be no single implementation which
simultaneously satisfies the semantics for both protocols. By renaming one
of the functions, we are able to provide separate implementations for each
requirement (which allows both protocols to function as intended).

True. However, putting on my critical hat, this seems like we're
stretching to provide support for an anti-pattern. It'd be nice to have an
example where one runs into the motivating problem *and* where the proposed
feature promotes a _better_ design than is currently possible, rather than
making a bad design compile.

At this point, I'm imagining scenarios where a user is trying to conform
a type MyAnimal to both Biped and Quadruped, then worrying that `walk()`
has two semantics: something has already gone deeply wrong IMO.

[Yes, I know there are animals that can sometimes walk on two or four
legs. The point here is that the protocols were clearly designed to model
animals at a certain level of detail, while it appears that the user
writing `MyAnimal` wants to model the animal at a different level of detail
than either protocol was designed to handle. You might have specific qualms
about this particular hypothetical, but I think you can pick out the
general point that there is a much larger problem inherent in the design
than the specific problem regarding two colliding method signatures.]

There may also be functions in different protocols with different names

but the same semantics and signature. This will allow a single
implementation to satisfy both protocols without duplication.

This is a poor argument IMO. You can already implement foo() and then
have bar() forward to foo() with trivial effort and really minimal
boilerplate. It's an existing solution, and a more general solution because
it doesn't require matching signatures. Also, it's a better solution IMO
because it preserves the notion that a type T : Fooable, Barrable provides
the full API guaranteed by Fooable and Barrable.

Finally, we may want to rename an inherited default implementation to

avoid conflicting with another protocol's default implementation in cases
where we don’t want to override it.

Yes, I think this would be handy. I can't think of an existing way to do
this, and I expect it might make a big difference in designing good
protocols. So here, I think we have a strong argument.

Again, though, could we find a concrete example of how this feature
would promoter a better design of an actual type and/or protocol?

One possibility is to allow a struct/class/enum to conform to the

protocol while renaming one (or both) of the clashing methods:

        struct C: A,B {
                var x:Int
                var y:Double implements B.x
        }

The conforming method/property would still have to have the same
signature, but could have a different name (and parameter labels). It
would also allow protocol methods which have identical signatures and
semantics, but different names to be implemented using the same method (i.e
‘implements D.z & E.w’).

When something is cast to the protocol (say ‘as B’), then calling the
property (e.g. ‘x’) would end up calling the implementation of the renamed
property ( ‘y’ in this example) on the conforming type.

Reflecting on this proposed change, it occurs to me that something of
value would be lost, and I think that this something is actually rather
valuable:

Today, when I see that a type conforms to (for example) Sequence, I
know that certain methods and/or properties exist on that type. Protocol
conformance guarantees a certain API, not just certain semantics.

It isn’t actually lost, however. When working with it as a Sequence
(for example), that API would be intact using the original names. It is
only when working with it as its own type that the renaming would have an
effect.

That is not my point. With this proposal, knowing that MyGreatType
conforms to Sequence would no longer yield any information as to the
MyGreatType API. That is definitely something lost and we should
acknowledge that.

Also, recall that Sequence has Self or associated type requirements. So:

let m = MyGreatType()
// There is nothing I can write here to use the Sequence API with `m`,
IIUC;
// however, depending on how this feature is designed, I *might* be able
to
// call a generic function that operates on a type `T : Sequence` and
work
// with `m` that way.

Perhaps one way to mitigate this loss would be to have any renamed

members listed *in the declaration of conformance*, something like this
(with some additional bikeshedding):

struct MyGreatType : Sequence (count => length) {
  // MyGreatType conforms to Sequence but renames `count` to `length`
}

Yes, putting it in the conformance declaration is a definite
possibility we should consider.

I think we would also want a way to retroactively conform using

existing properties/methods in an extension declaring conformance. Not
sure what the best syntax for that would be. Off the top of my head
(though I would love to have something with less cruft):

        extension D:B {
                @conform(to: B.x, with: D.y)
        }

or maybe just:

        extension D:B {
                D.y implements B.x
        }

If renamed members are declared along with protocol conformance, then
the syntax for retroactive modeling follows naturally:

extension D : B (x => y) { }
// again, the actual notation here is ugly
// but the underlying idea, I think, is worth considering

Yup

One thing I like about this is that it helps to solve the diamond
problem. ‘x’ could be a default implementation in B which D does not
override. I think this is an important case which my original proposal
didn’t address fully.

We should keep bikeshedding the syntax though...

Thanks,
Jon

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jon Hull) #19

Hi everyone,

We talked about this before when we were discussing mixins, and there seemed to be generally positive feelings towards it as a feature for the future.

It's been some time now since the original discussion, so perhaps you could refresh our collective memory (or at least, mine): although it *seems* like this feature might be useful, I can't recall a concrete use case where I've felt like I needed this feature--do you have some examples?

Ideally, the biggest use is that it helps to (partially) solve the diamond problem (and similar issues) by forcing/allowing disambiguation when there are multiple protocols being conformed to. This will become more of an issue if we allow protocols or extensions to add storage. Your proposed syntax actually does a better job of it than mine because mine was always shown as attached to some sort of implementation, whereas yours could potentially allow access to a default implementation under a new name.

Other than that, it generally allows us to bypass/mitigate conflicts between protocols. In the current version, you are unable to conform to both protocols (either because it won’t compile or because you can’t satisfy the semantics of both protocols) without designing the protocols together to avoid conflicts. (I have definitely had to go back and rename/refactor properties on a protocol for this reason… which I couldn’t have done if I didn’t control both protocols).

I understand something of the difficulty of confronting the diamond problem. As I wrote above, I'm inclined to believe that this proposed feature would help solve a real issue. However, the point I'm trying to make is that, on reflection, I have never actually been hampered by the lack of this feature, and so I'd like to continue the discussion to get a fuller sense of just how impactful this proposal would be, both positive and negative.

It's true, of course, that if you control at least one of two protocols (you don't need to control both protocols), it is trivially easy to cause this problem to occur, but as you point out it is also possible to resolve the problem by re-designing the protocol you control. I'm inclined to think (without evidence, admittedly) that re-designing to remove the conflict, where possible, would actually be the superior option in most cases.

My question was: have you actually run into a scenario that necessitates the feature you propose because you controlled neither conflicting protocol? I think it would strengthen the proposal greatly to have a concrete, uncontrived example.

Right now I commonly have to hand-namespace protocol methods/properties to avoid conflicts. So instead of having ‘var image:UIImage’ (which is the name which makes the most sense in the protocol’s context), I have ‘var protocolNameImage:UIImage’. There are lots of things which have common properties like ‘count’ which have to be called ‘somethingCount’ or ‘countOfSomething’. In the context of the protocol, these names are full of redundant words (especially when measured against the new naming guidelines). We are all used to doing this for Objective C, but it feels out of place in Swift.

This will become a much more serious issue as the third-party code ecosystem grows. Without some capability like this, you will have frameworks which can’t be used together (or at least with the same object). I would hate to see a ‘best practice’ emerge of adding 3 letter prefixes to all protocol methods to get around compatibility issues.

Ah, well this isn't exactly the diamond problem you're talking about here. Instead, I think, we have a fundamental disagreement. I think I've been told that this opinion of mine is 'insane'--but I hold to it:

Well you asked for an additional example besides the diamond problem… so no it isn’t. I did include a diamond problem example further down though…

Protocols are not merely a vehicle for delivering a reusable bag of code. One of its most essential purposes is to constrain the shape or API of its conforming types. Therefore, it is a feature, not a bug, that with every choice of name in a protocol you foreclose the possibility of composing that protocol with others that might have colliding names.

Currently, if you the protocol vendor have made the decision that `image` "makes the most sense in the protocol's context", you must have considered whether it would be absurd for a conforming type to have another use for `image`. If it would be absurd, then `image` is the appropriate name for your protocol requirement and any other word would truly be redundant. But, if this is only one of many plausible images, then `somethingImage` or `imageOfSomething` *is* the appropriate name, and trying to shorten the name isn't at all consistent with Swift guidelines but rather an incorrect attempt to prioritize brevity over clarity.

Most things that conform would have ‘image’, and it would have exactly the same semantics as my protocol. Thus their ‘image’ would provide conformance without additional work. But I have to worry about name collisions, so now I have to defensively call it ‘imageOfSomething', which they now have to implement to call their ‘image’ method.

What you're arguing is that protocol designers should be able to design protocols without regard for how they will compose with others in conforming types, relying on a new member-renaming feature instead. But, as you point out, you can already use a protocol as a mere bag of code by naming all members with unique, prefixed names, then have conforming types forward their own choice of names to these.

No, I am arguing that protocol authors should design protocols in the way which makes the behavior/semantics of the protocol the most obvious to the caller. 95% of the time there won’t be collisions, but occasionally there will be and we have to have a plan for that.

This member-renaming feature you propose would enhance the aesthetic pleasure of the protocol designer, allowing simple names that don't ever have to appear in the public API of a concrete type to be used for a protocol member without placing any restrictions on the API of conforming types. However, I don't see anything wrong with the current hand-prefixing method being enshrined as "best practice" for the bag-of-code approach to protocols. If, as you predict, a growing third-party code ecosystem makes name collisions worse, then in fact having uniquely distinguishable prefixed members would be less confusing than having conforming types renaming protocol members as a matter of course.

You are arguing that namespace collisions are a feature instead of a bug? Did you feel that way about ObjectiveC’s lack of name spacing?

I don’t think it is anywhere near as confusing as you suggest. As I mentioned before, if you cast it to the protocol, then the original names will still work. If you are trying to type the original name on the typed conformer, then showing the renamed version (with an indication of the renaming) in autocomplete should teach the change and clear up any confusion.

To take your example of walk(). Perhaps we have a protocol ‘Walkable’ which refers to any data structure where the nodes can be walked using the ‘walk()’ function. It is easy to imagine two different protocols A & B which specialize on this in different ways (say LinearWalkable & RandomWalkable), and both add some methods/properties and use those to provide efficient default implementations. At some point, you may run into a data structure which could easily be walked in both ways.

As things are right now, you couldn’t inherit from both protocols. While you could add new ‘linearWalk()’ & ‘randomWalk()’ to the protocols respectively (cluttering their interface), there is still the issue of what to do when 'walk()’ is called. You can’t rename walk() in the originating protocols because it comes from their common ancestor. Much better to force one (or both) of the methods to be renamed on the conforming data structure. That keeps the interfaces of the protocols clean and makes the options available on the data structure clearer (e.g. ‘walk()’ & ‘randomWalk()’ )

What I have had to do in the current version is inherit from the original protocol and then copy and paste the default implementations from the specialized versions. Now my code has been duplicated and is harder to maintain. We can do better.

I think Charles's solution is pretty nice, but he's right that the API surface area will have to grow. I don't know his original use case, so I don't know how ugly I'd find the final solution to be in that scenario. In this particular example, I'd say that having `linearWalk()` and `randomWalk()` distinguished seems pretty sensible and an overall win for clarity. If the same vendor controlled all three protocols, then `Walkable` could have the `walk()` requirement removed altogether for even more clarity.

So you would remove 'walk()' from Walkable to avoid the name collision in this one case, when ‘walk()’ is Walkable’s entire reason for being? Also, you would lose polymorphism.

Also, just a hunch, but I suspect your hypothetical would never hold. Could you envision how the requirements for RandomWalkable might be such that it's possible to implement an efficient _default_ implementation of a random walk for any of several conforming data structures, but only one of these data structures is LinearWalkable, _and_ such a linear walk is efficient using another _default_implementation for an overlapping but not identical set of data structures? It's not mere trivia here, because the crux of your argument is that there exist default implementations that require copying and pasting into conforming types (and sufficiently efficient default implementations so that copying and pasting is appropriate rather than implementing a more efficient version). More likely in diamond problem scenarios, I think, colliding members are going to be properties or methods either without default implementations or than need to supply more efficient versions of default implementations anyway.

Based on what? There is a reason the diamond problem has a name (and a wikipedia entry). Charles just said he ran into a problem like this. I have run into it in the past as well.

···

On Aug 24, 2016, at 7:48 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Wed, Aug 24, 2016 at 3:39 AM, Jonathan Hull <jhull@gbis.com <mailto:jhull@gbis.com>> wrote:

On Aug 23, 2016, at 8:35 AM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Tue, Aug 23, 2016 at 3:02 AM, Jonathan Hull <jhull@gbis.com <mailto:jhull@gbis.com>> wrote:

On Aug 22, 2016, at 11:32 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Mon, Aug 22, 2016 at 11:59 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:


(Xiaodi Wu) #20

Hi everyone,

We talked about this before when we were discussing mixins, and there
seemed to be generally positive feelings towards it as a feature for the
future.

It's been some time now since the original discussion, so perhaps you
could refresh our collective memory (or at least, mine): although it
*seems* like this feature might be useful, I can't recall a concrete use
case where I've felt like I needed this feature--do you have some examples?

Ideally, the biggest use is that it helps to (partially) solve the
diamond problem (and similar issues) by forcing/allowing disambiguation
when there are multiple protocols being conformed to. This will become
more of an issue if we allow protocols or extensions to add storage. Your
proposed syntax actually does a better job of it than mine because mine was
always shown as attached to some sort of implementation, whereas yours
could potentially allow access to a default implementation under a new name.

Other than that, it generally allows us to bypass/mitigate conflicts
between protocols. In the current version, you are unable to conform to
both protocols (either because it won’t compile or because you can’t
satisfy the semantics of both protocols) without designing the protocols
together to avoid conflicts. (I have definitely had to go back and
rename/refactor properties on a protocol for this reason… which I couldn’t
have done if I didn’t control both protocols).

I understand something of the difficulty of confronting the diamond
problem. As I wrote above, I'm inclined to believe that this proposed
feature would help solve a real issue. However, the point I'm trying to
make is that, on reflection, I have never actually been hampered by the
lack of this feature, and so I'd like to continue the discussion to get a
fuller sense of just how impactful this proposal would be, both positive
and negative.

It's true, of course, that if you control at least one of two protocols
(you don't need to control both protocols), it is trivially easy to cause
this problem to occur, but as you point out it is also possible to resolve
the problem by re-designing the protocol you control. I'm inclined to think
(without evidence, admittedly) that re-designing to remove the conflict,
where possible, would actually be the superior option in most cases.

My question was: have you actually run into a scenario that necessitates
the feature you propose because you controlled neither conflicting
protocol? I think it would strengthen the proposal greatly to have a
concrete, uncontrived example.

Right now I commonly have to hand-namespace protocol methods/properties
to avoid conflicts. So instead of having ‘var image:UIImage’ (which is the
name which makes the most sense in the protocol’s context), I have ‘var
protocolNameImage:UIImage’. There are lots of things which have common
properties like ‘count’ which have to be called ‘somethingCount’ or
‘countOfSomething’. In the context of the protocol, these names are full
of redundant words (especially when measured against the new naming
guidelines). We are all used to doing this for Objective C, but it feels
out of place in Swift.

This will become a much more serious issue as the third-party code
ecosystem grows. Without some capability like this, you will have
frameworks which can’t be used together (or at least with the same
object). I would hate to see a ‘best practice’ emerge of adding 3 letter
prefixes to all protocol methods to get around compatibility issues.

Ah, well this isn't exactly the diamond problem you're talking about here.
Instead, I think, we have a fundamental disagreement. I think I've been
told that this opinion of mine is 'insane'--but I hold to it:

Well you asked for an additional example besides the diamond problem… so
no it isn’t. I did include a diamond problem example further down though…

Sorry, I wasn't asking for an example _besides_ the diamond problem. I was
asking for more information about a concrete scenario, diamond problem or
not, where an existing technique could not resolve the conflict (for
instance, a scenario when you controlled neither of two conflicting
protocols, and where no satisfactory alternative design existed that could
avoid conforming a single type to both protocols).

Protocols are not merely a vehicle for delivering a reusable bag of code.
One of its most essential purposes is to constrain the shape or API of its
conforming types. Therefore, it is a feature, not a bug, that with every
choice of name in a protocol you foreclose the possibility of composing
that protocol with others that might have colliding names.

Currently, if you the protocol vendor have made the decision that `image`
"makes the most sense in the protocol's context", you must have considered
whether it would be absurd for a conforming type to have another use for
`image`. If it would be absurd, then `image` is the appropriate name for
your protocol requirement and any other word would truly be redundant. But,
if this is only one of many plausible images, then `somethingImage` or
`imageOfSomething` *is* the appropriate name, and trying to shorten the
name isn't at all consistent with Swift guidelines but rather an incorrect
attempt to prioritize brevity over clarity.

Most things that conform would have ‘image’, and it would have exactly the
same semantics as my protocol. Thus their ‘image’ would provide conformance
without additional work. But I have to worry about name collisions, so now
I have to defensively call it ‘imageOfSomething', which they now have to
implement to call their ‘image’ method.

What you're arguing is that protocol designers should be able to design
protocols without regard for how they will compose with others in
conforming types, relying on a new member-renaming feature instead. But, as
you point out, you can already use a protocol as a mere bag of code by
naming all members with unique, prefixed names, then have conforming types
forward their own choice of names to these.

No, I am arguing that protocol authors should design protocols in the way
which makes the behavior/semantics of the protocol the most obvious to the
caller. 95% of the time there won’t be collisions, but occasionally there
will be and we have to have a plan for that.

This member-renaming feature you propose would enhance the aesthetic
pleasure of the protocol designer, allowing simple names that don't ever
have to appear in the public API of a concrete type to be used for a
protocol member without placing any restrictions on the API of conforming
types. However, I don't see anything wrong with the current hand-prefixing
method being enshrined as "best practice" for the bag-of-code approach to
protocols. If, as you predict, a growing third-party code ecosystem makes
name collisions worse, then in fact having uniquely distinguishable
prefixed members would be less confusing than having conforming types
renaming protocol members as a matter of course.

You are arguing that namespace collisions are a feature instead of a bug?
Did you feel that way about ObjectiveC’s lack of name spacing?

My argument is about protocols specifically: I understand that a major
feature of protocols is that they make guarantees regarding the API of
conforming types. In rare cases, two guarantees may conflict, but I do not
consider that conflict to be a bug per se, as it is the inevitable result
of what it means to have guarantees, i.e. it is part and parcel of the
feature. In order to provide a way of resolving conflicting requirements in
protocols, your solution eliminates the API-guaranteeing feature of
protocols altogether.

I can't comment about Objective-C, because I've never written a single line
of it.

I don’t think it is anywhere near as confusing as you suggest. As I
mentioned before, if you cast it to the protocol, then the original names
will still work.

Except when you can't cast to a protocol existential, as is the case with
any protocol with Self or associated type requirements.

If you are trying to type the original name on the typed conformer, then
showing the renamed version (with an indication of the renaming) in
autocomplete should teach the change and clear up any confusion.

Mine isn't an argument about usability or learnability. It's a
philosophical/design point: what are protocols for? My answer: among other
uses, for constraining the API of conforming types. Perhaps this view is
incompatible with the view that protocols should support additional
mixin-like features.

To take your example of walk(). Perhaps we have a protocol ‘Walkable’

which refers to any data structure where the nodes can be walked using the
‘walk()’ function. It is easy to imagine two different protocols A & B
which specialize on this in different ways (say LinearWalkable &
RandomWalkable), and both add some methods/properties and use those to
provide efficient default implementations. At some point, you may run into
a data structure which could easily be walked in both ways.

As things are right now, you couldn’t inherit from both protocols. While
you could add new ‘linearWalk()’ & ‘randomWalk()’ to the protocols
respectively (cluttering their interface), there is still the issue of what
to do when 'walk()’ is called. You can’t rename walk() in the originating
protocols because it comes from their common ancestor. Much better to
force one (or both) of the methods to be renamed on the conforming data
structure. That keeps the interfaces of the protocols clean and makes the
options available on the data structure clearer (e.g. ‘walk()’ &
‘randomWalk()’ )

What I have had to do in the current version is inherit from the original
protocol and then copy and paste the default implementations from the
specialized versions. Now my code has been duplicated and is harder to
maintain. We can do better.

I think Charles's solution is pretty nice, but he's right that the API
surface area will have to grow. I don't know his original use case, so I
don't know how ugly I'd find the final solution to be in that scenario. In
this particular example, I'd say that having `linearWalk()` and
`randomWalk()` distinguished seems pretty sensible and an overall win for
clarity. If the same vendor controlled all three protocols, then `Walkable`
could have the `walk()` requirement removed altogether for even more
clarity.

So you would remove 'walk()' from Walkable to avoid the name collision in
this one case, when ‘walk()’ is Walkable’s entire reason for being?

No, `walk()` is not Walkable's entire reason for being. Protocols guarantee
semantics also. A `Walkable` protocol without any required members would
still have a reason for being: conforming types are walkable.

On the other hand, given that Walkable certainly would have associated type
requriements, if you considered that `walk()` _was_ Walkable's entire
reason for being *and* you could rename `walk()` in any conforming type,
how is that different from not having a `walk()` requirement at all?

Also, you would lose polymorphism.

Also, just a hunch, but I suspect your hypothetical would never hold.
Could you envision how the requirements for RandomWalkable might be such
that it's possible to implement an efficient _default_ implementation of a
random walk for any of several conforming data structures, but only one of
these data structures is LinearWalkable, _and_ such a linear walk is
efficient using another _default_implementation for an overlapping but not
identical set of data structures? It's not mere trivia here, because the
crux of your argument is that there exist default implementations that
require copying and pasting into conforming types (and sufficiently
efficient default implementations so that copying and pasting is
appropriate rather than implementing a more efficient version). More likely
in diamond problem scenarios, I think, colliding members are going to be
properties or methods either without default implementations or than need
to supply more efficient versions of default implementations anyway.

Based on what? There is a reason the diamond problem has a name (and a
wikipedia entry). Charles just said he ran into a problem like this. I
have run into it in the past as well.

Sure, and returning to my question above: could you share details about
where you've run into this?

···

On Wed, Aug 24, 2016 at 1:59 PM, Jonathan Hull <jhull@gbis.com> wrote:

On Aug 24, 2016, at 7:48 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Wed, Aug 24, 2016 at 3:39 AM, Jonathan Hull <jhull@gbis.com> wrote:

On Aug 23, 2016, at 8:35 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Aug 23, 2016 at 3:02 AM, Jonathan Hull <jhull@gbis.com> wrote:

On Aug 22, 2016, at 11:32 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Mon, Aug 22, 2016 at 11:59 PM, Jonathan Hull via swift-evolution < >>> swift-evolution@swift.org> wrote: