I was working with result builders when I noticed that the following code fails to compile:
struct List<each Value> { }
func buildEither<each TrueValue, each FalseValue>(first: repeat each TrueValue) -> List<repeat each TrueValue, repeat each FalseValue> {
.init()
}
func buildEither<each TrueValue, each FalseValue>(second: repeat each FalseValue) -> List<repeat each TrueValue, repeat each FalseValue> {
.init()
}
let foo = true ? buildEither(first: "") : buildEither(second: 1)
Result values in '? :' expression have mismatching types 'List<repeat each TrueValue, Int>' and 'List<String, repeat each FalseValue>'
Result values in '? :' expression have mismatching types 'List<String, repeat each FalseValue>' and 'List<String, repeat each FalseValue>'
Generic parameter 'each FalseValue' could not be inferred
Generic parameter 'each TrueValue' could not be inferred
This is a 'lil bit of a tricky scenario — Swift must infer the functions' return type based the parameters passed to them and the context they're used in. This is similar to what SwiftUI does with ConditionalView.
Parameter packs are sadly mostly just kinda broken.
struct List<each Value> { }
func buildEither<each TrueValue, each FalseValue>(
first: repeat each TrueValue,
falseType: repeat (each FalseValue).Type
) -> List<repeat each TrueValue, repeat each FalseValue> {
.init()
}
func buildEither<each TrueValue, each FalseValue>(
second: repeat each FalseValue,
trueType: repeat (each TrueValue).Type
)
-> List<repeat each TrueValue, repeat each FalseValue> {
.init()
}
// All of this compiles.
let first = buildEither(first: "", falseType: Int.self)
let second = buildEither(second: 1, trueType: String.self)
_ = true ? first : second
// Cannot convert value of type 'List<String, Int>' to type 'List<String, Int>' in coercion
buildEither(first: "", falseType: Int.self) as List<String, Int>
_ = true ? first
// Result values in '? :' expression have mismatching types 'List<String, Int>' and 'List<String, Int>'
: buildEither(second: 1, trueType: String.self)
It's good to know that this isn't just me. I just wrote a bunch of code that uses parameter packs pretty heavily, and that works, but it does seem to kind of... fall apart in this instance.
typealias PackerType = Packer<Never, Never>.Type
let packerType = f(Never.self) // Compiles
_ = packerType as PackerType // Compiles.
_ = f(Never.self) as PackerType // Type of expression is ambiguous without a type annotation
For what it's worth, this problem doesn't seem to happen with tuples:
func f<each T>(_: repeat (each T).Type)
-> (repeat each T, repeat each T).Type {
fatalError()
}
typealias PackerType = (Never, Never).Type
let packerType = f(Never.self) // Compiles.
_ = packerType as PackerType // Compiles.
_ = f(Never.self) as PackerType // Compiles.
This at least gives me a workaround — I can traffic around tuples in places where this is an issue and use functions to turn them into the structs I need.