Yes, I agree with Xiaodi here. I don’t think this particular example is 
particularly compelling. Especially because it’s not following the full 
evolution of the APIs and usage, which is critical for understanding how 
defaults should work.


Let's look at the evolution of an API and its consumers with the example of a 
BigInt:


struct BigInt: Integer {
  var storage: Array<Int> = []
}


which a consumer is using like:


func process(_ input: BigInt) -> BigInt { ... }
let val1 = process(BigInt())
let val2 = process(0) 


Ok that's all fairly straightforward. Now we decide that BigInt should expose 
its storage type for power-users:


struct BigInt<Storage: BinaryInteger = Int>: Integer {
  var storage: Array<Storage> = []
}


Let's make sure our consumer still works:


func process(_ input: BigInt) -> BigInt { ... }
let val1 = process(BigInt())
let val2 = process(0) 


Ok BigInt in process’s definition now means BigInt<Int>, so this still all 
works fine. Perfect!


But then the developer of the process function catches wind of this new power 
user feature, and wants to support it.
So they too become generic:


func process<T: BinaryInteger>(_ input: BigInt<T>) -> BigInt<T> { ... }


The usage sites are now more complicated, and whether they should compile is 
unclear:


let val1 = process(BigInt())
let val2 = process(0) 


For val1 you can take a hard stance with your rule: BigInt() means 
BigInt<Int>(), and that will work. But for val2 this rule doesn't work, because 
no one has written BigInt unqualified. However if you say that the 
`Storage=Int` default is allowed to participate in this expression, then we can 
still find the old behaviour by defaulting to it when we discover Storage is 
ambiguous.

We can also consider another power-user function:


func fastProcess(_ input: BigInt<Int64>) -> BigInt<Int64> { ... }
let val3 = fastProcess(BigInt())


Again, we must decide the interpretation of this. If we take the interpretation 
that BigInt() has an inferred type, then the type checker should discover that 
BigInt<Int64> is the correct result. If however we take stance that BigInt() 
means BigInt<Int>(), then we'll get a type checking error which our users will 
consider ridiculous: *of course* they wanted a BigInt<Int64> here!

We do however have the problem that this won’t work:


let temp = BigInt()
fastProcess(temp) // ERROR — expected BigInt<Int64>, found BigInt<Int> 


But that’s just as true for normal ints:


let temp = 0
takesAnInt64(temp) // ERROR — expected Int64, found Int


Such is the limit of Swift’s inference scheme.

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to