<div dir="ltr"><div><div>Thanks for your questions Xiaodi, I might have missed some scenarios.</div><div>I've had some rethinking and here are the conclusions. </div><div>It's a slight refinement of the idea I had in the previous mail. Bear with me :)</div><div><br></div><div>If we have</div><div><br></div><div> func process<T>(_ t: T) {}</div><div><br></div><div> process(5)</div><div> </div><div> compiler will infer T as Int.</div><div> </div><div> Now we introduce a default argument</div><div> </div><div> func process<T = Int64>(_ t: T) {}</div><div><br></div><div> and in order to keep source compatibility, if we do</div><div> </div><div> process(5)</div><div> </div><div> we must again infer T as Int. That means that the inference should have</div><div> priority over default arguments. That is in accordance with the rule that</div><div> we defined earlier: if you can’t infer a particular type, fill in a default.</div><div> We are able to infer particular type, Int, so we do that.</div><div><br></div><div> However, say we had</div><div><br></div><div> struct Storage<T>: Integer {</div><div> init(_ t: T)</div><div> }</div><div> </div><div> let s = Storage(5)</div><div> </div><div> and we wanted to introduce a default argument:</div><div> </div><div> struct Storage<T = Int64>: Integer {</div><div> init(_ t: T)</div><div> }</div><div> </div><div> What happens with `s`? This is essentially the same problem as previous,</div><div> so the solution should also be same - we must infer Int.</div><div> </div><div> Would that be confusing for a developer? Maybe, but what is the</div><div> alternative? Using the default would make no sense because then</div><div> </div><div> let s = Storage("ops")</div><div><br></div><div> would fail to compile. So inference in such cases is a must. Similar </div><div> problem would be observed with inheritance and/or protocol adoption.</div><div> </div><div> protocol P {}</div><div> struct R: P {}</div><div> </div><div> struct Storage<T = P>: Integer {</div><div> init(_ t: T)</div><div> }</div><div><br></div><div> let s = Storage(R())</div><div><br></div><div> Is T infered to R or to P? To keep source compatibility, we must infer R.</div><div> </div><div> In other words, I agree with you Xiaodi.</div><div> </div><div> Now to my argument about type declarations. Say we now do</div><div> </div><div> let s: Storage = Storage(R())</div><div><br></div><div> In that case T must be infered to P because type declaration must not be</div><div> affected by inference. Storage on the left must be treated as Storage<P>.</div><div> If that were not the case, consider what would happen if you upgrade local</div><div> variable to a propery.</div><div> </div><div> class T {</div><div> let s: Storage</div><div><br></div><div> init() {</div><div> s = Storage(R())</div><div> }</div><div> }</div><div><br></div><div> What is infered for T must not change by making such upgrade and allowing</div><div> type inferrence in initializer to affect propery type would make no sense.</div><div> </div><div> Thus, there has to be a rule that says:</div><div><br></div><div> (I) In type declarations, no inference happens. By omitting generic arguments</div><div> one accepts the defaults.</div><div> </div><div> And to repeat our second rule:</div><div> </div><div> (II) When instantiating generic type or calling generic function, by omitting</div><div> generic arguments one lets the compiler specify generic arguments by following</div><div> the principle: infer particular type if possible, fill in a default otherwise.</div><div><br></div><div> Let's go throught some more examples.</div><div> </div><div> Declaring a function like</div><div> </div><div> func clear(_ storage: Storage)</div><div> </div><div> assumes `storage` to be of type Storage<P> because of rule (I).</div><div> </div><div> Declaring a constant like</div><div> </div><div> let s = Storage(R())</div><div> </div><div> will infer `s` to Storage<R> becasue of (II), but </div><div> </div><div> let s: Storage = Storage(R())</div><div> </div><div> would be considered identical to</div><div> </div><div> let s: Storage<P> = Storage(R())</div><div><br></div><div> because T is defaulted to P and rule (I) is applied to the left hand side, so</div><div> rule (II) applies to right hand side by the infering type from left hand side.</div><div> </div><div> We must also consider:</div><div> </div><div> let s = Storage("123")</div><div> </div><div> This is simple. With rule (II) we infer Storage<String>. However, if we do</div><div> </div><div> let s: Storage = Storage("123")</div><div> </div><div> compiler must throw an error: cannot assign Storage<String> to Storage<P>.</div><div><br></div><div> Next, consider following type</div><div> </div><div> struct Storage<T = Int64>: Integer {</div><div> init()</div><div> }</div><div><br></div><div> If we do</div><div> </div><div> let s = Storage()</div><div> </div><div> we should apply rule (II). In this case, no particular type can be infered so</div><div> we will fill in the default. Meaning that `s` would be Storage<Int64>.</div><div><br></div><div> What about generic function calls? Let's use the older example.</div><div> </div><div> protocol P {}</div><div> struct R: P {}</div><div><br></div><div> struct Storage<T = P>: Integer {</div><div> init(_ t: T)</div><div> }</div><div><br></div><div> func clear<T>(_ storage: Storage<T>)</div><div> </div><div> If we do</div><div> </div><div> clear(Storage())</div><div> </div><div> we should use rule (II) to get the type. Here we don't have anything to infer</div><div> from so we should fill in the default. T = Storage<P>.</div><div> </div><div> Doing </div><div> </div><div> clear(Storage(R()))</div><div> </div><div> would use rule (II) to infer T as R.</div><div> </div><div> However, consider generic function with defaults:</div><div><br></div><div> func clear<T = R>(_ storage: Storage<T>)</div><div> </div><div> Now we have a defult R in function and a default P in the type. What if we now do</div><div><br></div><div> clear(Storage())</div><div><br></div><div> Should T be specialized to P or R? This is a conflict of defaults. I'd say it should be</div><div> resolved in the favour of function. In a way function that operates on type X could be </div><div> considered extension of that type and has more specific use case knowledge of its</div><div> arguments so the function preference should be accepted.</div><div> </div><div> So, trying to apply rule (II). There is nothing to infer, so we try to fill in a default.</div><div> We have two defaults - function says the default is R, struct says the default is P.</div><div> As we are resolving this in favour of the function - we chose R.</div><div> </div><div> Final example,</div><div><br></div><div> clear(Storage(R()))</div><div><br></div><div> There are no conflicts here. By applying rule (II) we directly infer type R and use it</div><div> regardless of the defaults. </div><div><br></div></div><div><br></div><div>Sorry for the long email, but hopefully it's now more understandable. Looking forward to your feedback.</div><div><br></div><div><br></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Jan 26, 2017 at 2:15 AM, Xiaodi Wu <span dir="ltr"><<a href="mailto:xiaodi.wu@gmail.com" target="_blank">xiaodi.wu@gmail.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"><div dir="ltr">Srdan, I'm afraid I don't understand your discussion. Can you simplify it for me by explaining your proposed solution in terms of Alexis's examples below?<div><br></div><div>```</div><div><span class="gmail-m_7207672311283310314m_3536373594951016063gmail-"><div>// Example 1: user supplied default is IntegerLiteralConvertible</div><div><br></div><div>func foo<T=Int64>(t: T) { ... }</div><div><br></div></span><div>foo(22)</div><span class="gmail-m_7207672311283310314m_3536373594951016063gmail-"><div>// ^</div><div>// |</div><div>// What type gets inferred here?</div></span><div>```</div><div><br></div><div>I believe that it is essential that the answer here be `Int` and not `Int64`.</div><div><br></div><div>My reasoning is: a user's code *must not* change because a library *adds* a default in a newer version. (As mentioned in several design docs, most recently the new ABI manifesto, defaults in Swift are safe to add without breaking source compatibility.)</div><div><br></div><div>Here, if version 1 of a library has `func foo<T>(t: T) { ... }`, then `foo(22)` must infer `T` to be `Int`. That's just the rule in Swift, and it would be severely source-breaking to change that. Therefore, if version 2 of that library has `func foo<T=Int64>(t: T) { ... }`, then `foo(22)` must still infer `T` to be `Int`.</div><div><br></div><div>Does your proposed solution have the same effect?</div><div><br></div><div>```</div><span class="gmail-m_7207672311283310314m_3536373594951016063gmail-"><div>// Example 2: user supplied default isn't IntegerLiteralConvertible</div><div><br></div><div>func bar<T=Character>(t: T) { ... }</div><div><br></div></span><div>bar(22)</div><span class="gmail-m_7207672311283310314m_3536373594951016063gmail-"><div>// ^</div><div>// |</div><div>// What type gets inferred here?</div></span></div><div>```</div><div><br></div><div>By the same reasoning as above, this ought to be `Int`. What would the answer be in your proposed solution?</div><div><div class="gmail-m_7207672311283310314m_3536373594951016063gmail-h5"><div><br></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Jan 25, 2017 at 2:07 PM, Srđan Rašić <span dir="ltr"><<a href="mailto:srdan.rasic@gmail.com" target="_blank">srdan.rasic@gmail.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"><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><span><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></span><span><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></span><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><span><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></span><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><div class="gmail-m_7207672311283310314m_3536373594951016063gmail-m_-5401352120290045636h5"><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></div></div>
</blockquote></div><br></div></div></div></div>
</blockquote></div><br></div></div>