two protocols with the same method name


(Grzegorz Leszek) #1

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!


(Jordan Rose) #2

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鈥ut 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


(Howard Lovatt) #3

I don鈥檛 really get what you are driving at. If I rework your example so that it runs:

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

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

class X: A, B {
聽聽聽聽var _ring = ":ring:"
聽聽聽聽var ring: String {
聽聽聽聽聽聽聽聽get {
聽聽聽聽聽聽聽聽聽聽聽聽return _ring
聽聽聽聽聽聽聽聽}
聽聽聽聽聽聽聽聽set {
聽聽聽聽聽聽聽聽聽聽聽聽self._ring = newValue
聽聽聽聽聽聽聽聽}
聽聽聽聽}
}
let x = X()
(x as A).ring // :ring:
(x as B).ring // :ring:
x.ring // :ring:
x.ring = ":bell:"
x.ring // :bell:
// (x as A).ring = ":bell:" Error, A doesn't have set

Then the behaviour is exactly what I would expect.

路路路

On 7 Jan 2016, at 9:18 PM, 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


(Joe Groff) #4

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

路路路

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!


(David Sweeris) #5

I do the 2nd case on purpose. Usually it鈥檚 to refine typealiases rather than for methods/variables, but I鈥檓 pretty sure I鈥檒l be dealing with those as well fairly soon.

路路路

On Jan 7, 2016, at 17:40, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

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鈥ut 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

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


(Brent Royal-Gordon) #6

I don鈥檛 really get what you are driving at.

The point is that, although `A` and `B` both require properties with the same name, they expect different semantics from that property. Let's maybe give these more concrete names so you can understand the idea:

聽聽protocol Marriageable {
聽聽聽聽var ring: String? { get set } // File name of image of this person's wedding ring.
聽聽}
聽聽protocol CallReceivable {
聽聽聽聽var ring: String? { get set } // File name of ringtone to be used for this person.
聽聽}

聽聽struct Person: Marriageable, CallReceivable {
聽聽聽聽var ring: String?
聽聽}

Of course a person is marriageable and can this have "a ring", and of course you can also receive a call from them and they can thus have "a ring". But in reality, the "ring" that satisfies one of these things will not work for the other. If your best friend gets married and you add an image of the ring, then the next time your friend calls you, the phone ringing screen will try to play a JPEG as an MP3.

The "ring" example is, of course, slightly contrived, but I'm sure you can imagine a similar problem with real names, where you end up using the same term for two different and incompatible things.

What the OP is basically asking is, when Swift sees the same type conforming to Marriageable and CallReceivable, should it optimistically assume that the `ring` properties they both require are compatible and allow the code to pass through without comment? Or should it pessimistically assume that the `ring` properties are incompatible and emit a warning or error about them?

路路路

--
Brent Royal-Gordon
Architechies


(Wallacy) #7

Storage properties on protocol or in extension plus something when call
maybe a solution:

聽聽聽聽聽聽聽protocol Marriageable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var foo:Int = 0; // actual a var.
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? // actual a var.
聽聽聽聽聽聽聽聽}
聽聽聽聽聽聽聽聽protocol CallReceivable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? // actual a var.
聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽struct Person: Marriageable, CallReceivable { }

OR
聽聽聽聽聽聽聽聽protocol Marriageable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? { get set }
聽聽聽聽聽聽聽聽}
聽聽聽聽聽聽聽聽protocol CallReceivable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? { get set }
聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽struct Person { }

聽聽聽聽聽聽聽extension Person: Marriageable{
聽聽聽聽聽聽聽聽聽var ring: String?
聽聽聽聽聽聽聽}

聽聽聽聽聽extension Person: CallReceivable{
聽聽聽聽聽聽聽聽聽var ring: String?
聽聽聽聽聽聽聽}

So:

var person = Person()
person.foo = 1 // ok

person.ring = getRingtone() // error, ring is ambiguous
(person as CallReceivable).ring = getRingtone() // ok
OR
person.CallReceivable.ring = getRingtone() // ok | CallReceivable is a
know person protocol, so can get a implicity dot notation,
like .dynamicType.staticMethod();

Anyway, it's not an easy problem to solve.

路路路

Em s谩b, 9 de jan de 2016 脿s 01:09, Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> escreveu:

> I don鈥檛 really get what you are driving at.

The point is that, although `A` and `B` both require properties with the
same name, they expect different semantics from that property. Let's maybe
give these more concrete names so you can understand the idea:

聽聽聽聽聽聽聽聽protocol Marriageable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? { get set } // File name of image of
this person's wedding ring.
聽聽聽聽聽聽聽聽}
聽聽聽聽聽聽聽聽protocol CallReceivable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? { get set } // File name of ringtone
to be used for this person.
聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽struct Person: Marriageable, CallReceivable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String?
聽聽聽聽聽聽聽聽}

Of course a person is marriageable and can this have "a ring", and of
course you can also receive a call from them and they can thus have "a
ring". But in reality, the "ring" that satisfies one of these things will
not work for the other. If your best friend gets married and you add an
image of the ring, then the next time your friend calls you, the phone
ringing screen will try to play a JPEG as an MP3.

The "ring" example is, of course, slightly contrived, but I'm sure you can
imagine a similar problem with real names, where you end up using the same
term for two different and incompatible things.

What the OP is basically asking is, when Swift sees the same type
conforming to Marriageable and CallReceivable, should it optimistically
assume that the `ring` properties they both require are compatible and
allow the code to pass through without comment? Or should it
pessimistically assume that the `ring` properties are incompatible and emit
a warning or error about them?

--
Brent Royal-Gordon
Architechies

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


(Zhao Xin) #8

Howard is almost right.

Protocol A means the ring variable must have a getter. For the setter, you
can have it or not. Both won't violate the conformance of the Protocol A.

Actually, you can write as

protocol B:A {
var ring:String { get set }
}

The is more for the last line
// (x as A).ring = ":bell:" Error, A doesn't have set

You will find more that
(x as B).ring = "1"
doesn't work either. The reason is that (x as B) is a let by default.

You need do
var b = x as B
b.ring = "1"

Above works.

zhaoxin

路路路

On Sat, Jan 9, 2016 at 10:19 AM, Howard Lovatt via swift-evolution < swift-evolution@swift.org> wrote:

I don鈥檛 really get what you are driving at. If I rework your example so
that it runs:

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

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

class X: A, B {
聽聽聽聽var _ring = ":ring:"
聽聽聽聽var ring: String {
聽聽聽聽聽聽聽聽get {
聽聽聽聽聽聽聽聽聽聽聽聽return _ring
聽聽聽聽聽聽聽聽}
聽聽聽聽聽聽聽聽set {
聽聽聽聽聽聽聽聽聽聽聽聽self._ring = newValue
聽聽聽聽聽聽聽聽}
聽聽聽聽}
}
let x = X()
(x as A).ring // :ring:
(x as B).ring // :ring:
x.ring // :ring:
x.ring = ":bell:"
x.ring // :bell:
// (x as A).ring = ":bell:" Error, A doesn't have set

Then the behaviour is exactly what I would expect.

On 7 Jan 2016, at 9:18 PM, 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

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

--

Owen Zhao


(Yang Wu) #9

That's totally wrong!

聽聽聽聽聽聽聽聽protocol Marriageable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ringImageFileName: String? { get set } // File name
of image of this person's wedding ring.
聽聽聽聽聽聽聽聽}
聽聽聽聽聽聽聽聽protocol CallReceivable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ringtoneFileName: String? { get set } // File name of
ringtone to be used for this person.
聽聽聽聽聽聽聽聽}

路路路

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

> I don鈥檛 really get what you are driving at.

The point is that, although `A` and `B` both require properties with the
same name, they expect different semantics from that property. Let's maybe
give these more concrete names so you can understand the idea:

聽聽聽聽聽聽聽聽protocol Marriageable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? { get set } // File name of image of
this person's wedding ring.
聽聽聽聽聽聽聽聽}
聽聽聽聽聽聽聽聽protocol CallReceivable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? { get set } // File name of ringtone
to be used for this person.
聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽struct Person: Marriageable, CallReceivable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String?
聽聽聽聽聽聽聽聽}

Of course a person is marriageable and can this have "a ring", and of
course you can also receive a call from them and they can thus have "a
ring". But in reality, the "ring" that satisfies one of these things will
not work for the other. If your best friend gets married and you add an
image of the ring, then the next time your friend calls you, the phone
ringing screen will try to play a JPEG as an MP3.

The "ring" example is, of course, slightly contrived, but I'm sure you can
imagine a similar problem with real names, where you end up using the same
term for two different and incompatible things.

What the OP is basically asking is, when Swift sees the same type
conforming to Marriageable and CallReceivable, should it optimistically
assume that the `ring` properties they both require are compatible and
allow the code to pass through without comment? Or should it
pessimistically assume that the `ring` properties are incompatible and emit
a warning or error about them?

--
Brent Royal-Gordon
Architechies

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

--
Best Regards!

Yang Wu
--------------------------------------------------------
Location: Pudong, Shanghai, China.
EMail : pinxue@gmail.com
Website: http://www.time2change.mobi http://rockplayer.com
Twitter/Weibo : @pinxue
<http://www.pinxue.net>


(Howard Lovatt) #10

Thanks, I understand what you are driving at now.

This is true in many languages that have interfaces (protocols), e.g. Java and duck-typed languages, it doesn鈥檛 seem to be a big deal. Languages that have traits or mixins, e.g. Scala, can deal with this.

I suggest that it isn鈥檛 a big enough problem to worry about. It might go away if protocols morph into traits :).

路路路

On 9 Jan 2016, at 2:09 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

I don鈥檛 really get what you are driving at.

The point is that, although `A` and `B` both require properties with the same name, they expect different semantics from that property. Let's maybe give these more concrete names so you can understand the idea:

聽聽protocol Marriageable {
聽聽聽聽var ring: String? { get set } // File name of image of this person's wedding ring.
聽聽}
聽聽protocol CallReceivable {
聽聽聽聽var ring: String? { get set } // File name of ringtone to be used for this person.
聽聽}

聽聽struct Person: Marriageable, CallReceivable {
聽聽聽聽var ring: String?
聽聽}

Of course a person is marriageable and can this have "a ring", and of course you can also receive a call from them and they can thus have "a ring". But in reality, the "ring" that satisfies one of these things will not work for the other. If your best friend gets married and you add an image of the ring, then the next time your friend calls you, the phone ringing screen will try to play a JPEG as an MP3.

The "ring" example is, of course, slightly contrived, but I'm sure you can imagine a similar problem with real names, where you end up using the same term for two different and incompatible things.

What the OP is basically asking is, when Swift sees the same type conforming to Marriageable and CallReceivable, should it optimistically assume that the `ring` properties they both require are compatible and allow the code to pass through without comment? Or should it pessimistically assume that the `ring` properties are incompatible and emit a warning or error about them?

--
Brent Royal-Gordon
Architechies


(Zhao Xin) #11

I know what you mean at the beginning. However, as your example shows, the
warning means nothing if it really happens.

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.

zhaoxin

路路路

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

> I don鈥檛 really get what you are driving at.

The point is that, although `A` and `B` both require properties with the
same name, they expect different semantics from that property. Let's maybe
give these more concrete names so you can understand the idea:

聽聽聽聽聽聽聽聽protocol Marriageable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? { get set } // File name of image of
this person's wedding ring.
聽聽聽聽聽聽聽聽}
聽聽聽聽聽聽聽聽protocol CallReceivable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? { get set } // File name of ringtone
to be used for this person.
聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽struct Person: Marriageable, CallReceivable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String?
聽聽聽聽聽聽聽聽}

Of course a person is marriageable and can this have "a ring", and of
course you can also receive a call from them and they can thus have "a
ring". But in reality, the "ring" that satisfies one of these things will
not work for the other. If your best friend gets married and you add an
image of the ring, then the next time your friend calls you, the phone
ringing screen will try to play a JPEG as an MP3.

The "ring" example is, of course, slightly contrived, but I'm sure you can
imagine a similar problem with real names, where you end up using the same
term for two different and incompatible things.

What the OP is basically asking is, when Swift sees the same type
conforming to Marriageable and CallReceivable, should it optimistically
assume that the `ring` properties they both require are compatible and
allow the code to pass through without comment? Or should it
pessimistically assume that the `ring` properties are incompatible and emit
a warning or error about them?

--
Brent Royal-Gordon
Architechies

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

--

Owen Zhao


(Thorsten Seitz) #12

In Eiffel you would have to rename at least one of the conflicting members in the implementing class to distinguish the cases there.
When called through one of the protocols the correct renamed member would be used, i.e. (translated to pseudo-Swift):

protocol Marriageable {
聽聽var ring: String? { get set } // File name of image of this person's wedding ring.
}

protocol CallReceivable {
聽聽var ring: String? { get set } // File name of ringtone to be used for this person.
}

// In this example I choose to rename both
struct Person: Marriageable rename ring to ringImage, CallReceivable rename ring to ringTone {
聽聽var ringImage: String?
聽聽var ringTone: String?
}

let friend: Person = ...
let m: Marriageable = friend
let callee: CallReceivable = friend

friend.ring // type error (the Person type only has vars ringImage and ringTone)
friend.ringImage // file name of image of wedding ring
friend.ringTone // file name of ring tone
m.ring // result of friend.ringImage because of rename
callee.ring // result of friend.ringTone because of rename

This always does the right thing and solves the problem nicely.

-Thorsten

路路路

Am 09.01.2016 um 04:09 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org>:

I don鈥檛 really get what you are driving at.

The point is that, although `A` and `B` both require properties with the same name, they expect different semantics from that property. Let's maybe give these more concrete names so you can understand the idea:

聽聽protocol Marriageable {
聽聽聽聽var ring: String? { get set } // File name of image of this person's wedding ring.
聽聽}
聽聽protocol CallReceivable {
聽聽聽聽var ring: String? { get set } // File name of ringtone to be used for this person.
聽聽}

聽聽struct Person: Marriageable, CallReceivable {
聽聽聽聽var ring: String?
聽聽}

Of course a person is marriageable and can this have "a ring", and of course you can also receive a call from them and they can thus have "a ring". But in reality, the "ring" that satisfies one of these things will not work for the other. If your best friend gets married and you add an image of the ring, then the next time your friend calls you, the phone ringing screen will try to play a JPEG as an MP3.

The "ring" example is, of course, slightly contrived, but I'm sure you can imagine a similar problem with real names, where you end up using the same term for two different and incompatible things.

What the OP is basically asking is, when Swift sees the same type conforming to Marriageable and CallReceivable, should it optimistically assume that the `ring` properties they both require are compatible and allow the code to pass through without comment? Or should it pessimistically assume that the `ring` properties are incompatible and emit a warning or error about them?

--
Brent Royal-Gordon
Architechies

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


(Yang Wu) #13

Good point.

Protocol is a contract, it is natural to allow same item in multiple
protocol and eventually pointing to one single implementation.

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

}

If a type conforms BidirectionalIndexType, it gets all these advancedBy().
OK, for this specific case we may workaround by assuming safe for those
inherited. Which means we only warn for same name item from protocols in
different protocol hierarchy without common root.

It is still doubtful even for this filtered case, because compiler cannot
tell it appears by intention or by mistake. We have to suppress it for
acceptable cases, that's annoying as protocol should allow it naturally.

There is a case good for throwing error: receiving item with same name but
different type from two protocols, but it is already an error now.

路路路

On Sat, Jan 9, 2016 at 11:47 AM, Wallacy via swift-evolution < swift-evolution@swift.org> wrote:

Storage properties on protocol or in extension plus something when call
maybe a solution:

聽聽聽聽聽聽聽protocol Marriageable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var foo:Int = 0; // actual a var.
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? // actual a var.
聽聽聽聽聽聽聽聽}
聽聽聽聽聽聽聽聽protocol CallReceivable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? // actual a var.
聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽struct Person: Marriageable, CallReceivable { }

OR
聽聽聽聽聽聽聽聽protocol Marriageable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? { get set }
聽聽聽聽聽聽聽聽}
聽聽聽聽聽聽聽聽protocol CallReceivable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? { get set }
聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽struct Person { }

聽聽聽聽聽聽聽extension Person: Marriageable{
聽聽聽聽聽聽聽聽聽var ring: String?
聽聽聽聽聽聽聽}

聽聽聽聽聽extension Person: CallReceivable{
聽聽聽聽聽聽聽聽聽var ring: String?
聽聽聽聽聽聽聽}

So:

var person = Person()
person.foo = 1 // ok

person.ring = getRingtone() // error, ring is ambiguous
(person as CallReceivable).ring = getRingtone() // ok
OR
person.CallReceivable.ring = getRingtone() // ok | CallReceivable is a
know person protocol, so can get a implicity dot notation,
like .dynamicType.staticMethod();

Anyway, it's not an easy problem to solve.

Em s谩b, 9 de jan de 2016 脿s 01:09, Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org> escreveu:

> I don鈥檛 really get what you are driving at.

The point is that, although `A` and `B` both require properties with the
same name, they expect different semantics from that property. Let's maybe
give these more concrete names so you can understand the idea:

聽聽聽聽聽聽聽聽protocol Marriageable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? { get set } // File name of image of
this person's wedding ring.
聽聽聽聽聽聽聽聽}
聽聽聽聽聽聽聽聽protocol CallReceivable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String? { get set } // File name of ringtone
to be used for this person.
聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽struct Person: Marriageable, CallReceivable {
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽var ring: String?
聽聽聽聽聽聽聽聽}

Of course a person is marriageable and can this have "a ring", and of
course you can also receive a call from them and they can thus have "a
ring". But in reality, the "ring" that satisfies one of these things will
not work for the other. If your best friend gets married and you add an
image of the ring, then the next time your friend calls you, the phone
ringing screen will try to play a JPEG as an MP3.

The "ring" example is, of course, slightly contrived, but I'm sure you
can imagine a similar problem with real names, where you end up using the
same term for two different and incompatible things.

What the OP is basically asking is, when Swift sees the same type
conforming to Marriageable and CallReceivable, should it optimistically
assume that the `ring` properties they both require are compatible and
allow the code to pass through without comment? Or should it
pessimistically assume that the `ring` properties are incompatible and emit
a warning or error about them?

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

--
Best Regards!

Yang Wu
--------------------------------------------------------
Location: Pudong, Shanghai, China.
EMail : pinxue@gmail.com
Website: http://www.time2change.mobi http://rockplayer.com
Twitter/Weibo : @pinxue
<http://www.pinxue.net>


(Grzegorz Leszek) #14

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鈥ut 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


(Andrew Bennett) #15

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


(Thorsten Seitz) #16

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


(Brent Royal-Gordon) #17

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


(Trent Nadeau) #18

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
https://play.rust-lang.org/?gist=b7def3a2cd375ee80525&version=stable 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


(Brent Royal-Gordon) #19

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鈥攐ne 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


(Andrew Bennett) #20

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

https://msdn.microsoft.com/en-us/library/ms173157.aspx

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鈥攐ne 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