Skip to content

Instantly share code, notes, and snippets.

@dabrahams
Last active August 4, 2021 18:28
Show Gist options
  • Save dabrahams/d4caaa3fbd1458b9c4923cb1c3cd426b to your computer and use it in GitHub Desktop.
Save dabrahams/d4caaa3fbd1458b9c4923cb1c3cd426b to your computer and use it in GitHub Desktop.
Ambiguity problems with giving generic types the right behavior with conditional conformances
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.
// 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 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