two protocols with the same method name

Exactly there are two cases. I am working on a legacy project (aka
working effective with legacy code). It is written in objective-c.
There is a real life example where it was a problem. Of course it is
different language, but I have stared experimenting with swift and
found the same behavior. Object was conforming to 10 protocols. I was
just expecting different behavior of the class looking at the first
protocol.

I agree that it was easy to find a problem, yes it was different
language and yes it was bad naming and bad code. But we do not always
design every single class and protocol. Devs on the project have
different level of knowledge. Swift is new, so it is not a problem
now, but legacy code evolves after years and years. I think it would
be really great if Swift as a protocol oriented language could give a
hint that protocols declare the same requirements. Similar to warning
'unused variable'.

Brent ask a really good question: 'so we have to decide if "two
unrelated protocols declare the same requirement" is a strong enough
signal on its own to conclude that something's wrong'.
I believe it is strong enough.

Below is another example, similar to what I was struggling with:

protocol PrivatePhotos {
  func photos() -> String
}
protocol PublicPhotos {
  func photos() -> String
}
class Flickr: PublicPhotos {
}
extension Flickr: PrivatePhotos {
  func photos() -> String {
    return ":rage:"
  }
}
class Newspaper {
  func printPaper(stock: PublicPhotos) {
    print(stock.photos())
  }
}
let stock: PublicPhotos = Flickr()
let dailyNews = Newspaper()
dailyNews.printPaper(stock) // private photos will be printed

Greg

···

2016-01-08 1:40 GMT+00:00 Jordan Rose <jordan_rose@apple.com>:

Mm. There are two cases here:

- Two protocols use the same method name for completely unrelated things.
- Two protocols use the same method name for similar things.

In the latter case, having one method satisfy both requirements is probably fine. In the former, there would definitely be a problem…but I have a hard time thinking of a type that should actually conform to two protocols that do completely different things and yet manage to use the same name for something. Do you have a real-life example where this has been a problem? Everything I can find online looks contrived.

Jordan

On Jan 7, 2016, at 2:18, Grzegorz Leszek via swift-evolution <swift-evolution@swift.org> wrote:

I suggest compile warning if one classes/structs/enums implements
protocols with the same name.
It could lead to confusions, when methods of those protocols will mean
different things.
It will force to implement parent protocol with shared methods or
change method in one of the protocols.
Below is an example.
Regards,
Greg

//:ring:
protocol A {
var ring: String { get }
}

//:bell:
protocol B {
var ring: String { get set }
}

class X: A, B {
var ring: String {
   get {
     return ":ring:"
   }
   set {
     self.ring = newValue
   }
}
}
let x = X()
let somewhereInTheProject = "\(x.ring) the bell"
x.ring = ":bell:" // ERROR!
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

1 Like

I like this approach, @implements is nice, clear, concise and handles many
cases.

It will require your proposal, or similar, to be universally applicable
though, but I expect it will pass: Naming Functions with Argument Labels.

Many Cocoa delegate protocols will have similar function names, ie.
collectionView(...).

···

On Thursday, 14 January 2016, Joe Groff via swift-evolution < swift-evolution@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>> wrote:

> On Jan 7, 2016, at 2:18 AM, Grzegorz Leszek via swift-evolution < > swift-evolution@swift.org> wrote:
>
> I suggest compile warning if one classes/structs/enums implements
> protocols with the same name.
> It could lead to confusions, when methods of those protocols will mean
> different things.
> It will force to implement parent protocol with shared methods or
> change method in one of the protocols.
> Below is an example.
> Regards,
> Greg
>
> //:ring:
> protocol A {
> var ring: String { get }
> }
>
> //:bell:
> protocol B {
> var ring: String { get set }
> }
>
> class X: A, B {
> var ring: String {
> get {
> return ":ring:"
> }
> set {
> self.ring = newValue
> }
> }
> }
> let x = X()
> let somewhereInTheProject = "\(x.ring) the bell"
> x.ring = ":bell:" // ERROR!

Swift's protocol conformance model doesn't rely on the name of the member
matching the name of the requirement it satisfies. One possibility here is
to introduce an attribute to explicitly declare what protocol
requirement(s) a member is intended to satisfy:

class X: A, B {
  @implements(A.ring)
  var weddingRing: String

  @implements(B.ring)
  var ringtone: String
}

As other noted, protocols with same-named requirements but different
semantics are rare in practice, and it's occasionally useful to
intentionally overlap requirements (I believe the CollectionType hierarchy
does this in places), so the current behavior feels like a reasonable
default to me. I can see value in requiring it to be explicit though.

-Joe

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

Nice!

-Thorsten

···

Am 14.01.2016 um 00:48 schrieb Joe Groff via swift-evolution <swift-evolution@swift.org>:

On Jan 7, 2016, at 2:18 AM, Grzegorz Leszek via swift-evolution <swift-evolution@swift.org> wrote:

I suggest compile warning if one classes/structs/enums implements
protocols with the same name.
It could lead to confusions, when methods of those protocols will mean
different things.
It will force to implement parent protocol with shared methods or
change method in one of the protocols.
Below is an example.
Regards,
Greg

//:ring:
protocol A {
var ring: String { get }
}

//:bell:
protocol B {
var ring: String { get set }
}

class X: A, B {
var ring: String {
  get {
    return ":ring:"
  }
  set {
    self.ring = newValue
  }
}
}
let x = X()
let somewhereInTheProject = "\(x.ring) the bell"
x.ring = ":bell:" // ERROR!

Swift's protocol conformance model doesn't rely on the name of the member matching the name of the requirement it satisfies. One possibility here is to introduce an attribute to explicitly declare what protocol requirement(s) a member is intended to satisfy:

class X: A, B {
@implements(A.ring)
var weddingRing: String

@implements(B.ring)
var ringtone: String
}

As other noted, protocols with same-named requirements but different semantics are rare in practice, and it's occasionally useful to intentionally overlap requirements (I believe the CollectionType hierarchy does this in places), so the current behavior feels like a reasonable default to me. I can see value in requiring it to be explicit though.

-Joe

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

Because you cannot solve the problem by revising your own code. You definitely have to change the protocols. If you have no way to change the protocols, you will have to drop either of them.

My point is that it is the protocols' programmer who should take care of it, not you. Or if you write the protocols, you should avoid this at the beginning.

Well, that's exactly the point. In a perfect world, where the compiler could read your mind and figure out that the two protocols are incompatible, you would want Swift to raise an error indicating that the two protocols can't be used together. But the compiler can't read your mind, so we have to decide if "two unrelated protocols declare the same requirement" is a strong enough signal on its own to conclude that something's wrong.

···

--
Brent Royal-Gordon
Architechies

Wallacy's solution above is similar to how Rust handles a very similar
situation. Rust errors out where there are conflicting implementations of
trait requirements in the same "scope". See
Rust Playground for an
example.

···

On Sat, Jan 9, 2016 at 3:05 PM, Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

> Because you cannot solve the problem by revising your own code. You
definitely have to change the protocols. If you have no way to change the
protocols, you will have to drop either of them.
>
> My point is that it is the protocols' programmer who should take care of
it, not you. Or if you write the protocols, you should avoid this at the
beginning.

Well, that's exactly the point. In a perfect world, where the compiler
could read your mind and figure out that the two protocols are
incompatible, you would want Swift to raise an error indicating that the
two protocols can't be used together. But the compiler can't read your
mind, so we have to decide if "two unrelated protocols declare the same
requirement" is a strong enough signal on its own to conclude that
something's wrong.

--
Brent Royal-Gordon
Architechies

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

--
Trent Nadeau

By giving warning simply for same name, it will be quite annoying when the project run into this situation without any wrong. For example:

protocol ForwardIndexType : _Incrementable {
    @warn_unused_result
    public func advancedBy(n: Self.Distance) -> Self
}

extension ForwardIndexType {
    @warn_unused_result
    public func advancedBy(n: Self.Distance) -> Self
    @warn_unused_result
    public func advancedBy(n: Self.Distance, limit: Self) -> Self
    @warn_unused_result
    public func distanceTo(end: Self) -> Self.Distance
}

protocol BidirectionalIndexType : ForwardIndexType
extension BidirectionalIndexType {
    @warn_unused_result
    public func advancedBy(n: Self.Distance) -> Self
    @warn_unused_result
    public func advancedBy(n: Self.Distance, limit: Self) -> Self
}

Firstly, for methods and subscriptors the "name" would actually encompass the entire signature, so `advancedBy(_:)` and `advancedBy(_:limit:)` would not conflict because they have different signatures.

Secondly, `ForwardIndexType` and `BidirectionalIndexType` are *not* unrelated protocols—one of them conforms to the other. Thus, we can assume that `BidirectionalIndexType` knows about `ForwardIndexType`'s `advancedBy` methods and intends for its versions to have compatible semantics.

If instead `BidirectionalIndexType` did *not* conform to `ForwardIndexType`, and `RandomAccessIndexType` tried to conform to both `ForwardIndexType` and `BidirectionalIndexType`, *then* we would get an error, because two independent protocols would have declared `advancedBy(_: Self.Distance) -> Self` methods and it's possible they meant for them to have different semantics.

···

--
Brent Royal-Gordon
Architechies

Sorry if this is already mentioned, but I quite like C#'s "Explicit
Interface Implementation" approach:

Basically:

var Marriageable.ring: String? { ... }
var CallReceivable.ring: String? { ... }

to call it you could do self.ring if it was unambiguous, otherwise:

(self as Marriageable).ring
(self as CallReceivable).ring

···

On Mon, Jan 11, 2016 at 9:51 AM, Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

> By giving warning simply for same name, it will be quite annoying when
the project run into this situation without any wrong. For example:
>
> protocol ForwardIndexType : _Incrementable {
> @warn_unused_result
> public func advancedBy(n: Self.Distance) -> Self
> }
>
> extension ForwardIndexType {
> @warn_unused_result
> public func advancedBy(n: Self.Distance) -> Self
> @warn_unused_result
> public func advancedBy(n: Self.Distance, limit: Self) -> Self
> @warn_unused_result
> public func distanceTo(end: Self) -> Self.Distance
> }
>
> protocol BidirectionalIndexType : ForwardIndexType
> extension BidirectionalIndexType {
> @warn_unused_result
> public func advancedBy(n: Self.Distance) -> Self
> @warn_unused_result
> public func advancedBy(n: Self.Distance, limit: Self) -> Self
> }

Firstly, for methods and subscriptors the "name" would actually encompass
the entire signature, so `advancedBy(_:)` and `advancedBy(_:limit:)` would
not conflict because they have different signatures.

Secondly, `ForwardIndexType` and `BidirectionalIndexType` are *not*
unrelated protocols—one of them conforms to the other. Thus, we can assume
that `BidirectionalIndexType` knows about `ForwardIndexType`'s `advancedBy`
methods and intends for its versions to have compatible semantics.

If instead `BidirectionalIndexType` did *not* conform to
`ForwardIndexType`, and `RandomAccessIndexType` tried to conform to both
`ForwardIndexType` and `BidirectionalIndexType`, *then* we would get an
error, because two independent protocols would have declared `advancedBy(_:
Self.Distance) -> Self` methods and it's possible they meant for them to
have different semantics.

--
Brent Royal-Gordon
Architechies

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

Brent ask a really good question: 'so we have to decide if "two
unrelated protocols declare the same requirement" is a strong enough
signal on its own to conclude that something's wrong'.
I believe it is strong enough.

Also, as I've thought about it more, I've realized that if we allow protocol extensions to add conformances, we can cleanly resolve the cases where two protocols *don't* conflict. For instance, if PrivatePhotos and PublicPhotos are supposed to be compatible:

  protocol PhotosType {
    func photos() -> String
  }
  extension PrivatePhotos: PhotosType {}
  extension PublicPhotos: PhotosType {}
  
  // Now that the relationship between PrivatePhotos and PublicPhotos has been retroactively modeled,
  // we can conform one type to both protocols.

···

--
Brent Royal-Gordon
Architechies

I Like this C#'s "Explicit Interface Implementation".
The cast to call is understandable, it's good, but just think about. If we
use "self.Marriageable.ring" or "variable.Marriageable.ring" will make more
symmetrical with the implementation? "dot protocol" can be used to all
"explicit call", ambiguous or not.

···

Em dom, 10 de jan de 2016 às 21:49, Andrew Bennett via swift-evolution < swift-evolution@swift.org> escreveu:

Sorry if this is already mentioned, but I quite like C#'s "Explicit
Interface Implementation" approach:

Explicit Interface Implementation - C# Programming Guide - C# | Microsoft Learn

Basically:

var Marriageable.ring: String? { ... }
var CallReceivable.ring: String? { ... }

to call it you could do self.ring if it was unambiguous, otherwise:

(self as Marriageable).ring
(self as CallReceivable).ring

On Mon, Jan 11, 2016 at 9:51 AM, Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org> wrote:

> By giving warning simply for same name, it will be quite annoying when
the project run into this situation without any wrong. For example:
>
> protocol ForwardIndexType : _Incrementable {
> @warn_unused_result
> public func advancedBy(n: Self.Distance) -> Self
> }
>
> extension ForwardIndexType {
> @warn_unused_result
> public func advancedBy(n: Self.Distance) -> Self
> @warn_unused_result
> public func advancedBy(n: Self.Distance, limit: Self) -> Self
> @warn_unused_result
> public func distanceTo(end: Self) -> Self.Distance
> }
>
> protocol BidirectionalIndexType : ForwardIndexType
> extension BidirectionalIndexType {
> @warn_unused_result
> public func advancedBy(n: Self.Distance) -> Self
> @warn_unused_result
> public func advancedBy(n: Self.Distance, limit: Self) -> Self
> }

Firstly, for methods and subscriptors the "name" would actually encompass
the entire signature, so `advancedBy(_:)` and `advancedBy(_:limit:)` would
not conflict because they have different signatures.

Secondly, `ForwardIndexType` and `BidirectionalIndexType` are *not*
unrelated protocols—one of them conforms to the other. Thus, we can assume
that `BidirectionalIndexType` knows about `ForwardIndexType`'s `advancedBy`
methods and intends for its versions to have compatible semantics.

If instead `BidirectionalIndexType` did *not* conform to
`ForwardIndexType`, and `RandomAccessIndexType` tried to conform to both
`ForwardIndexType` and `BidirectionalIndexType`, *then* we would get an
error, because two independent protocols would have declared `advancedBy(_:
Self.Distance) -> Self` methods and it's possible they meant for them to
have different semantics.

--
Brent Royal-Gordon
Architechies

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

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

I don't think self.Marriageable.ring is a good idea. As self.Type
conforms Marriageable protocol, and a protocol is not a property of a
class/struct/enum.

Let's go back with the definition of the Protocol.

A protocol defines a blueprint of methods, properties, and other

requirements that suit a particular task or piece of functionality. The
protocol can then be adopted by a class, structure, or enumeration to
provide an actual implementation of those requirements. Any type that
satisfies the requirements of a protocol is said to conform to that
protocol.

In addition to specifying requirements that conforming types must
implement, you can extend a protocol to implement some of these
requirements or to implement additional functionality that conforming types
can take advantage of.

So if there are some independent protocols accidentally sharing the same
vars or functions, this does not violate the definition of the protocol.
Should the compiler warns the programmer? Maybe, as we can not sure there
must be conflicts. So I think the alert level is between warning and
silence.

Let's also see the solutions. The solutions in other languages inspire us
what to do. One is to change the properties names, the other is to cast the
type to the protocol.

I think the prior method is much easier to go with. As currently, you
can't name a store properties in extension, and you can't have two
properties with the same name in extension. However, there is also a
problem after you rename the property. As it is no longer the name that
defined in the protocol, you can use it with (self as Protocol).name. So
maybe we can use lable to solve the problem.

Basically:
           type A: Marriageable, CallReceivable {

     var Marriageable.ring ring: String? { ... }
     var CallReceivable.ring ringtone: String? { ... }
}

to call it :

(self as Marriageable).ring or self.ring
(self as CallReceivable).ring or self.ringtone

zhaoxin

···

On Mon, Jan 11, 2016 at 8:17 AM, Wallacy via swift-evolution < swift-evolution@swift.org> wrote:

I Like this C#'s "Explicit Interface Implementation".
The cast to call is understandable, it's good, but just think about. If we
use "self.Marriageable.ring" or "variable.Marriageable.ring" will make more
symmetrical with the implementation? "dot protocol" can be used to all
"explicit call", ambiguous or not.

Em dom, 10 de jan de 2016 às 21:49, Andrew Bennett via swift-evolution < > swift-evolution@swift.org> escreveu:

Sorry if this is already mentioned, but I quite like C#'s "Explicit
Interface Implementation" approach:

Explicit Interface Implementation - C# Programming Guide - C# | Microsoft Learn

Basically:

var Marriageable.ring: String? { ... }
var CallReceivable.ring: String? { ... }

to call it you could do self.ring if it was unambiguous, otherwise:

(self as Marriageable).ring
(self as CallReceivable).ring

On Mon, Jan 11, 2016 at 9:51 AM, Brent Royal-Gordon via swift-evolution < >> swift-evolution@swift.org> wrote:

> By giving warning simply for same name, it will be quite annoying when
the project run into this situation without any wrong. For example:
>
> protocol ForwardIndexType : _Incrementable {
> @warn_unused_result
> public func advancedBy(n: Self.Distance) -> Self
> }
>
> extension ForwardIndexType {
> @warn_unused_result
> public func advancedBy(n: Self.Distance) -> Self
> @warn_unused_result
> public func advancedBy(n: Self.Distance, limit: Self) -> Self
> @warn_unused_result
> public func distanceTo(end: Self) -> Self.Distance
> }
>
> protocol BidirectionalIndexType : ForwardIndexType
> extension BidirectionalIndexType {
> @warn_unused_result
> public func advancedBy(n: Self.Distance) -> Self
> @warn_unused_result
> public func advancedBy(n: Self.Distance, limit: Self) -> Self
> }

Firstly, for methods and subscriptors the "name" would actually
encompass the entire signature, so `advancedBy(_:)` and
`advancedBy(_:limit:)` would not conflict because they have different
signatures.

Secondly, `ForwardIndexType` and `BidirectionalIndexType` are *not*
unrelated protocols—one of them conforms to the other. Thus, we can assume
that `BidirectionalIndexType` knows about `ForwardIndexType`'s `advancedBy`
methods and intends for its versions to have compatible semantics.

If instead `BidirectionalIndexType` did *not* conform to
`ForwardIndexType`, and `RandomAccessIndexType` tried to conform to both
`ForwardIndexType` and `BidirectionalIndexType`, *then* we would get an
error, because two independent protocols would have declared `advancedBy(_:
Self.Distance) -> Self` methods and it's possible they meant for them to
have different semantics.

--
Brent Royal-Gordon
Architechies

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

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

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

--

Owen Zhao

I agree with zhaoxin, self.Marriageable.ring doesn't seem to provide the
same interface as the protocol. It is similar to how it would be done with
existing swift code, and doesn't risk sharing a name with static properties
and typealiases on the type.

Something we still need to handle though is associated and Self type
requirements.
* Should you be able to apply similar namespaces to an associated type?
* How do you cast to a protocol with an associated type?

For example:

protocol P1 {
    typealias Element

    func get() -> Element

}
protocol P2 {
    typealias Element
    func get() -> Element
}
struct X: P1, P2 {
    typealias P1.Element = Int
    typealias P2.Element = Float
    func get() -> P1.Element { fatalError() }
    func get() -> P2.Element { fatalError() }
}
struct Y: P1, P2 {
    typealias Element = Int
    func P1.get() -> Element { fatalError() }
    func P2.get() -> Element { fatalError() }
}

let e1 = (x as P1).get()
let e2 = (x as P2).get()

There will be errors on e1 and e2 like this:

*protocol 'P1' can only be used as a generic constraint because it has Self
or associated type requirements*

You may be able to get around it with a generic function, but the ugliness
of that solution should be considered in our choices for this proposal.

It would be nice (in general) to be able to do this:

let e3 = (x as P1<Int>).get()
let e4 = (x as P2<Float>).get()

However it's unclear what type e3 and e4 should be, there's currently no
support for automatic synthesis of type-erased structures, and there's no
meta-type I know of that can stand-in.

Perhaps associated and Self type requirements are out-of-scope of this
proposal, but I thought it was worth mentioning.

···

On Mon, Jan 11, 2016 at 4:26 PM, 肇鑫 <owenzx@gmail.com> wrote:

I don't think self.Marriageable.ring is a good idea. As self.Type
conforms Marriageable protocol, and a protocol is not a property of a
class/struct/enum.

Let's go back with the definition of the Protocol.

A protocol defines a blueprint of methods, properties, and other

requirements that suit a particular task or piece of functionality. The
protocol can then be adopted by a class, structure, or enumeration to
provide an actual implementation of those requirements. Any type that
satisfies the requirements of a protocol is said to conform to that
protocol.

In addition to specifying requirements that conforming types must
implement, you can extend a protocol to implement some of these
requirements or to implement additional functionality that conforming types
can take advantage of.

So if there are some independent protocols accidentally sharing the same
vars or functions, this does not violate the definition of the protocol.
Should the compiler warns the programmer? Maybe, as we can not sure there
must be conflicts. So I think the alert level is between warning and
silence.

Let's also see the solutions. The solutions in other languages inspire us
what to do. One is to change the properties names, the other is to cast the
type to the protocol.

I think the prior method is much easier to go with. As currently, you
can't name a store properties in extension, and you can't have two
properties with the same name in extension. However, there is also a
problem after you rename the property. As it is no longer the name that
defined in the protocol, you can use it with (self as Protocol).name. So
maybe we can use lable to solve the problem.

Basically:
           type A: Marriageable, CallReceivable {

     var Marriageable.ring ring: String? { ... }
     var CallReceivable.ring ringtone: String? { ... }
}

to call it :

(self as Marriageable).ring or self.ring
(self as CallReceivable).ring or self.ringtone

zhaoxin

On Mon, Jan 11, 2016 at 8:17 AM, Wallacy via swift-evolution < > swift-evolution@swift.org> wrote:

I Like this C#'s "Explicit Interface Implementation".
The cast to call is understandable, it's good, but just think about. If
we use "self.Marriageable.ring" or "variable.Marriageable.ring" will make
more symmetrical with the implementation? "dot protocol" can be used to all
"explicit call", ambiguous or not.

Em dom, 10 de jan de 2016 às 21:49, Andrew Bennett via swift-evolution < >> swift-evolution@swift.org> escreveu:

Sorry if this is already mentioned, but I quite like C#'s "Explicit
Interface Implementation" approach:

Explicit Interface Implementation - C# Programming Guide - C# | Microsoft Learn

Basically:

var Marriageable.ring: String? { ... }
var CallReceivable.ring: String? { ... }

to call it you could do self.ring if it was unambiguous, otherwise:

(self as Marriageable).ring
(self as CallReceivable).ring

On Mon, Jan 11, 2016 at 9:51 AM, Brent Royal-Gordon via swift-evolution >>> <swift-evolution@swift.org> wrote:

> By giving warning simply for same name, it will be quite annoying
when the project run into this situation without any wrong. For example:
>
> protocol ForwardIndexType : _Incrementable {
> @warn_unused_result
> public func advancedBy(n: Self.Distance) -> Self
> }
>
> extension ForwardIndexType {
> @warn_unused_result
> public func advancedBy(n: Self.Distance) -> Self
> @warn_unused_result
> public func advancedBy(n: Self.Distance, limit: Self) -> Self
> @warn_unused_result
> public func distanceTo(end: Self) -> Self.Distance
> }
>
> protocol BidirectionalIndexType : ForwardIndexType
> extension BidirectionalIndexType {
> @warn_unused_result
> public func advancedBy(n: Self.Distance) -> Self
> @warn_unused_result
> public func advancedBy(n: Self.Distance, limit: Self) -> Self
> }

Firstly, for methods and subscriptors the "name" would actually
encompass the entire signature, so `advancedBy(_:)` and
`advancedBy(_:limit:)` would not conflict because they have different
signatures.

Secondly, `ForwardIndexType` and `BidirectionalIndexType` are *not*
unrelated protocols—one of them conforms to the other. Thus, we can assume
that `BidirectionalIndexType` knows about `ForwardIndexType`'s `advancedBy`
methods and intends for its versions to have compatible semantics.

If instead `BidirectionalIndexType` did *not* conform to
`ForwardIndexType`, and `RandomAccessIndexType` tried to conform to both
`ForwardIndexType` and `BidirectionalIndexType`, *then* we would get an
error, because two independent protocols would have declared `advancedBy(_:
Self.Distance) -> Self` methods and it's possible they meant for them to
have different semantics.

--
Brent Royal-Gordon
Architechies

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

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

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

--

Owen Zhao

1 Like

Are you sure you can write implementation code in protocol declaration as your first solution displayed? And are you sure you can write stored properties in extension as your second solution displayed? yeah, you really handle problem in an easy way, not because the problem is such easy thing. :slight_smile:

Have you ever resolved this problem?

In my opinion, this is just like the classic problem "diamond inheritance". Since the protocol support multiple inheritance, the problem will arise sooner or later. :slight_smile:

What I like about internet conversations, is that I can carry on after 4 years as if nothing had happened :slight_smile:

I did solve the problem 'instantly' by refactoring/renaming the protocols.
However my point was that it was hard to find out on a spot what caused the issue.
As you identified correctly, it was the classic problem of Deadly Diamond of Death.

Uh-huh! It seems like, if the two protocols have properties with the same name, and the property really meaning different thing, we have to change one protocol property with another name. Awesome! But what if we cannot change the protocol, for example, the protocol definition comes from third party modules. I know the situation is rare, but I still remains. :joy:

Whatever we do, we need to source/binary compatibility to consider now.

Could we say that a protocol’s requirements can optionally be defined with the protocol’s name as a prefix if you don’t just want to use your property/func/etc with the same name?

protocol A {
    var foo: Int {get}
}

protocol B {
    var foo: Int {get}
}

struct C {
    var foo: Int
    var bar: Int
}

To conform C to A and B, you could write the same thing you’d write now:

extension C: A, B {}

or you could do something different if you and the author of A disagree on the semantics of foo

extension C: A, B {
    var A.foo: Int { bar }
    // no need to specify `B.foo`
}

You could also specify them directly in the type of you want the protocol’s properties to have different storage than your own properties of the same name:

struct D: A, B {
    var foo: Int // same as `A.foo`
    var B.foo: Int // different storage from `self.foo`
}

In generic code, you’d get the version associated with the protocol constraints:

func baz<T: A>(_ v: T) -> Int {
    v.foo // returns `v.A.foo`
}
func buz<T: B>(_ v: T) -> Int {
    v.foo // returns `v.B.foo`
}

It’d need some disambiguation in generic code in which the type is constrained to both protocols:

func faz<T: A&B>(_ v: T) -> Int {
    v.foo // does it return `v.A.foo‘ or `v.B.foo`?
}

I would say that if there isn’t a “custom” implementation for either protocol conformance, then the compiler knows that both v.A.foo‘ and v.B.foo` are the same thing and it doesn’t matter. As soon as one of the protocol conformances gets “customized”, the compiler can’t know which one you mean and you’d have to specify:

func faz<T: A&B>(_ v: T) -> Int {
    v.A.foo + v.B.foo // `v.foo` would be an error.
}

In generic code where the type is constrained to be a subclass of a class which contains a property with the same name as a protocol property and it has a “custom” conformance, the non-prefixed property refers to the original storage:

class E: A {
    var foo: Int
    var A.foo: Int { foo - 10 }
}

func fez<T: E>(_ v: T) -> (Int, Int) {
    (v.A.foo, v.foo)
}

let e = E(foo: 20)
fez(e) // returns (10, 20)

Dunno, it’s oh wow, 2:45am here. Apologies if I missed something obvious. I might prefer @Joe_Groff‘s @implements(A.foo) var foobaz: Int syntax... not sure... it’s longer, but it’s maybe clearer. Plus it seems like having it be an attribute might maybe align with some hypothetical future code generation or macro system where maybe you want to, um, log something every time you call a function that’s the same name as a protocol requirement but isn’t the same implementation? I’m not sure why you’d want to do that, but whatever, it’s 2:52am now and my think has stopped braining.

1 Like

I have good news! This syntax was implemented at some point in the last 5 years!

protocol A {
    var foo: Int {get}
}
protocol B {
    var foo: Int {get}
}
struct C: A, B {
    @_implements(A, foo)
    var fooForA: Int
    @_implements(B, foo)
    var fooForB: Int
}
let c = C(fooForA: 1, fooForB: 2)
let ca: A = c
let cb: B = c
// print(c.foo) // error: ambiguous use of 'fooForB'
print(ca.foo) // 1
print(cb.foo) // 2

Unfortunately it is underscored, which means it's not officially supported. There may be some unsupported edge cases, it can go away after we have something official, your warranty is void.

It is a good starting point for proposing something official

8 Likes

Oh, cool! BRB, gotta go try some stuff

Just a heads up—this is a several-years-old thread which was migrated from the former Swift mailing lists. The etiquette on this forum in such cases is generally to start a new thread (linking to the old one, if desired/necessary for context) to avoid notifying all former participants (who may or may not wish to be receiving emails still).

2 Likes