<div dir="ltr">That's a good example Alexis. I do agree that generic arguments are inferred in a lot of cases, my point was that they should not be inferred in "type declarations". Not sure what's the right terminology here, but I mean following places:<div><br></div><div>(I) Variable/Constant declaration</div><div><br></div><div> ```</div><div> let x: X</div><div> ```</div><div><br></div><div>(II) Property declaration</div><div><br></div><div> ```</div><div> struct T {</div><div> let x: X</div><div> }</div><div> ```</div><div><br></div><div>(III) Function declaration</div><div><br></div><div> ```</div><div> func a(x: X) -> X</div><div> ```</div><div><br></div><div><div>(IV) Enumeration case declaration</div><div><br></div><div> ```</div><div> enum E {</div><div> case x(X)</div><div> }</div><div> ```</div></div><div><br></div><div><div>(V) Where clauses</div><div><br></div><div> ```</div><div> extensions E where A == X {} </div><div> ```</div></div><div><br></div><div>In those cases `X` should always mean `X<Int>` if it was defined as `struct X<T = Int>`. That's all my rule says. Sorry for not being clear in the last email :)</div><div><br></div><div>As for the other cases, mostly those where an instance is created, inference should be applied.</div><div><br></div><div>Let's go through your examples. Given</div><div><br></div><div><span style="font-size:12.800000190734863px">struct BigInt: Integer {</span><br style="font-size:12.800000190734863px"><span style="font-size:12.800000190734863px"> var storage: Array<Int> = []</span><br style="font-size:12.800000190734863px"><span style="font-size:12.800000190734863px">}</span><br></div><div><br></div><div><span style="font-size:12.800000190734863px">func process<T: BinaryInteger>(_ input: BigInt<T>) -> BigInt<T> { ... }</span><br style="font-size:12.800000190734863px"></div><div><br></div><div><span style="font-size:12.800000190734863px">what happens with `</span><span style="font-size:12.800000190734863px">let val1 = process(BigInt())`? </span><span style="font-size:12.800000190734863px">I think this is actually the same problem as what happens in case of `let x = BigInt()`.</span></div><div><br></div><div><span style="font-size:12.800000190734863px">In such case my rule does not apply as we don't have full type declaration. In </span><span style="font-size:12.800000190734863px">`let x = BigInt()` type is not defined at all, while in `</span><span style="font-size:12.800000190734863px">func process<T: BinaryInteger>(_ input: BigInt<T>) -> BigInt<T> { ... }` </span><span style="font-size:12.800000190734863px">type is explicitly weakened or "undefaulted" if you will. </span></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">We should introduce new rule for such cases and allowing `Storage=Int` default to participate in such expressions would make sense. As you said, it also solves second example: </span><span style="font-size:12.800000190734863px">let val2 = process(0).</span></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">I guess this would be the problem we thought we were solving initially and in that case I think the solution should be what Doug suggested: </span><span style="font-size:12.800000190734863px">if you can’t infer a particular type, fill in a default.</span></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">Of course, if the default conflicts with the generic constraint, it would not be filled in and it would throw an error.</span></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">For the sake of completeness,</span></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">func fastProcess(_ input: BigInt<Int64>) -> BigInt<Int64> { ... }</span><br style="font-size:12.800000190734863px"><span style="font-size:12.800000190734863px">let val3 = fastProcess(BigInt())</span><span style="font-size:12.800000190734863px"><br></span></div><div><br></div><div>would certainly infer the type from context as my rule does not apply to initializers. It would infer BigInt<Int64>.</div><div><br></div><div>As for your last example, I guess we can't do anything about that and that's ok.</div><div><br></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Jan 25, 2017 at 7:50 PM, Alexis <span dir="ltr"><<a href="mailto:abeingessner@apple.com" target="_blank">abeingessner@apple.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">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.<br>
<br>
<br>
Let's look at the evolution of an API and its consumers with the example of a BigInt:<br>
<br>
<br>
struct BigInt: Integer {<br>
var storage: Array<Int> = []<br>
}<br>
<br>
<br>
which a consumer is using like:<br>
<br>
<br>
func process(_ input: BigInt) -> BigInt { ... }<br>
let val1 = process(BigInt())<br>
let val2 = process(0)<br>
<br>
<br>
Ok that's all fairly straightforward. Now we decide that BigInt should expose its storage type for power-users:<br>
<br>
<br>
struct BigInt<Storage: BinaryInteger = Int>: Integer {<br>
var storage: Array<Storage> = []<br>
}<br>
<br>
<br>
Let's make sure our consumer still works:<br>
<br>
<br>
func process(_ input: BigInt) -> BigInt { ... }<br>
let val1 = process(BigInt())<br>
let val2 = process(0)<br>
<br>
<br>
Ok BigInt in process’s definition now means BigInt<Int>, so this still all works fine. Perfect!<br>
<br>
<br>
But then the developer of the process function catches wind of this new power user feature, and wants to support it.<br>
So they too become generic:<br>
<br>
<br>
func process<T: BinaryInteger>(_ input: BigInt<T>) -> BigInt<T> { ... }<br>
<br>
<br>
The usage sites are now more complicated, and whether they should compile is unclear:<br>
<br>
<br>
let val1 = process(BigInt())<br>
let val2 = process(0)<br>
<br>
<br>
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.<br>
<br>
We can also consider another power-user function:<br>
<br>
<br>
func fastProcess(_ input: BigInt<Int64>) -> BigInt<Int64> { ... }<br>
let val3 = fastProcess(BigInt())<br>
<br>
<br>
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!<br>
<br>
We do however have the problem that this won’t work:<br>
<br>
<br>
let temp = BigInt()<br>
fastProcess(temp) // ERROR — expected BigInt<Int64>, found BigInt<Int><br>
<br>
<br>
But that’s just as true for normal ints:<br>
<br>
<br>
let temp = 0<br>
takesAnInt64(temp) // ERROR — expected Int64, found Int<br>
<br>
<br>
Such is the limit of Swift’s inference scheme.<br>
<br>
</blockquote></div><br></div></div>