Yes, I used let _ = () just as an example (it was the simplest and most meaningless statement I could come up with).
Here's a further reduced demonstration program:
func foo<T, R>(_ a: T, _ fn: (T) -> R) -> R { return fn(a) }
let a: [(id: Int, data: String)] = [(123, "abc")]
let b: [(id: Int, daata: String)] = foo(a) {
// _ = () // <-- Uncomment this line to detect error on
return $0 // <-- this line at compile- instead of run time.
}
So, for Array, this is an error that shows either at runtime or compile time (depending on the above workaround).
But for other simple generic types like G here:
struct G<T> { var value: T }
func foo<T, R>(_ a: T, _ fn: (T) -> R) -> R { return fn(a) }
let a: G<(id: Int, data: String)> = G(value: (123, "abc"))
let b: G<(id: Int, daata: String)> = foo(a) {
return $0 // <-- Error here at compile time without the above workaround.
}
the error will always be detected at compile time, ie even without the meaningless-statement-workaround.
But (again) for Optional, though the error will be detected at compile time with the meaningless-statement-workaround, it will not be detected at runtime. So the type conversion from
Optional<(id: Int, data: String)> to
Optional<(id: Int, daata: String)>
will just work:
func foo<T, R>(_ a: T, _ fn: (T) -> R) -> R { return fn(a) }
let a: (id: Int, data: String)? = (123, "abc")
let b: (id: Int, daata: String)? = foo(a) {
// _ = () // <-- Uncomment for compile time error on the next line, but note that it will compile and run as long as this line is commented away.
return $0 // <-- No runtime error here when it's `Optional<Tuple>` instead of `Array<Tuple>`.
}
print(b ?? "") // Prints `(id: 123, daata: "abc")`
I guess it all boils down to type inference resulting in (what at least we perceive as) inconsistent behavior. That it sometimes succeeds is probably because it then manages to infer the types so that something along the lines of the following example happens (which is valid and compiling code):
let a: (id: Int, data: String) = (123, "abc")
let b: (id: Int, daata: String) = { (x: (Int, String)) in x }(a)
But that can't be the whole explanation, since it would mean something like this for your original example:
let myData: [[(id: Int, data: String)]] = [
[(100,"Apple"),(200,"Orange")],
[(300,"Lemon")]
]
let myFlatData1: [(id: Int, daata: String)] = myData.reduce([]) {
(a: [(Int, String)], b: [(Int, String)]) in return a + b
}
print(myFlatData1) // Prints `[(id: 100, daata: "Apple"), (id: 200, daata: "Orange"), (id: 300, daata: "Lemon")]`
which compiles and runs without a runtime error ...
Anyway, I filed SR-12261.
Perhaps @Slava_Pestov can explain what's going on?