Last active
August 4, 2021 18:28
-
-
Save dabrahams/d4caaa3fbd1458b9c4923cb1c3cd426b to your computer and use it in GitHub Desktop.
Ambiguity problems with giving generic types the right behavior with conditional conformances
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
protocol P { var pop: String { get } } | |
extension P { | |
var pop: String { "slow" } | |
var pop1: String { pop } | |
} | |
protocol Q: P {} | |
extension Q { var pop: String { "fastQ" } } | |
protocol R: P {} | |
extension R { var pop: String { "fastR" } } | |
struct X<T>: P {} | |
extension X: Q where T: Equatable {} | |
extension X: R where T: CustomStringConvertible {} | |
struct Y: Equatable, CustomStringConvertible { | |
var description: String { "Y" } | |
} | |
print(X<Y>().pop1) // "slow", but IMO should choose one fast implementation, deterministically. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// If you think mentioning X<Y> in Example1.swift should | |
// be a compiler error, read this example and then proceed | |
// to the next one. Otherwise, there's no point in looking | |
// at Example2.swift or Example3.swift | |
// This example is just like the previous one except that | |
// the ambiguity is based on two separate generic parameters | |
// to X. | |
protocol P { var pop: String { get } } | |
extension P { | |
var pop: String { "slow" } | |
var pop1: String { pop } | |
} | |
protocol Q: P {} | |
extension Q { var pop: String { "fastQ" } } | |
protocol R: P {} | |
extension R { var pop: String { "fastR" } } | |
// ======= Difference from Example1 starts here ========== | |
struct X<T, U>: P {} | |
extension X: Q where T: Equatable {} | |
extension X: R where U: CustomStringConvertible {} | |
struct Y: Equatable { } | |
struct Z: CustomStringConvertible { | |
var description: String { "Z" } | |
} | |
print(X<Y, Z>().pop1) // "slow", but IMO should choose one fast implementation, deterministically. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This example is just like the previous one, except that | |
// we use the inability of Swift to monomorphize generic code | |
// to create X<Y, Z> without ever mentioning it. | |
protocol P { var pop: String { get } } | |
extension P { | |
var pop: String { "slow" } | |
var pop1: String { pop } | |
} | |
protocol Q: P {} | |
extension Q { var pop: String { "fastQ" } } | |
protocol R: P {} | |
extension R { var pop: String { "fastR" } } | |
struct X<T, U>: P {} | |
extension X: Q where T: Equatable {} | |
extension X: R where U: CustomStringConvertible {} | |
struct Y: Equatable { } | |
struct Z: CustomStringConvertible { | |
var description: String { "Z" } | |
} | |
// ======= Difference from Example2 starts here ========== | |
extension X { | |
var asQ: X<Y, U> { .init() } | |
var asR: X<T, Z> { .init() } | |
func pop2(_ n: Int) -> String { | |
return n == 1 ? "\(Self.self): \(pop1)" | |
: n == 2 ? asQ.pop2(n - 1) | |
: asR.pop2(n - 1) | |
} | |
} | |
enum Inert {} | |
// When run as "swift Example3.swift 1 2", prints "X<Y, Z>: slow" | |
print(X<Inert, Inert>().pop2(CommandLine.arguments.count)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment