Is there a way to statically guarantee a rock-paper-scissors relationship using protocols such that a type can't beat itself?
For example, we could define the protocol relationship as:
protocol ThreeCycle {
associatedtype Beats: ThreeCycle where Beats.Beats.Beats == Self
}
But then we're allowed to compile a type which can beat itself directly:
struct PowerfulRock: ThreeCycle {
typealias Beats = PowerfulRock
}
Can we disallow the associatedtype Beats from equaling Self?
Alternatively we could split the types into three protocols:
protocol RockType {
associatedtype Beats: ScissorType where Beats.Beats.Beats == Self
}
protocol ScissorType {
associatedtype Beats: PaperType where Beats.Beats.Beats == Self
}
protocol PaperType {
associatedtype Beats: RockType where Beats.Beats.Beats == Self
}
But again we're allowed to compile a type which can beat itself directly:
struct PowerfulRock2: RockType, ScissorType, PaperType {
typealias Beats = PowerfulRock2
}
In this case, can we disallow a type from conforming to a specific combination of protocols?
No. Since we allow retroactive conformances I don't think this would be possible in general.
jrose
(Jordan Rose)
3
But the actual problem seems pretty straightforward with three protocols:
associatedtype Next: Paper
associatedtype Next: Scissors
associatedtype Next: Rock
Thanks for the response Slava. What stops the compiler from checking if a type is not allowed to retroactively conform to a protocol? For example, we provide the constraint that any type conforming to protocol A can't also conform to protocol B. If we create a type T: A and extend it later to conform to B, why can't the compiler raise an error at that point?
Joe_Groff
(Joe Groff)
5
How about this. You can take advantage of the fact that a type can only (non-retroactively) conform to a protocol once with one associated type binding to do something like this
protocol ThreeCycle {
associatedtype Beats: ThreeCycle where Beats.Beats.Beats == Self
associatedtype BeatMarker
}
protocol RockType: ThreeCycle where Beats: ScissorType, BeatMarker == any ScissorType { }
protocol ScissorType: ThreeCycle where Beats: PaperType, BeatMarker == any PaperType { }
protocol PaperType: ThreeCycle where Beats: RockType, BeatMarker == any RockType { }
The same-type constraints on the BeatMarker associated type make it impossible for one conformance to satisfy more than one of the RockType, ScissorType, or PaperType protocol requirements.
8 Likes
Thanks for your response Joe! That's a great answer. It also gives a way to create an XOR between protocols
protocol XOR {
associatedtype Marker
}
protocol A: XOR where Marker == any A {
func doThis()
}
protocol B: XOR where Marker == any B {
func doThat()
}
struct Both: A, B { //Type 'Both' does not conform to protocol 'A', 'B' or 'Exclude'
func doThis() {}
func doThat() {}
}