<div dir="ltr"><div><div>Thanks for your questions Xiaodi, I might have missed some scenarios.</div><div>I&#39;ve had some rethinking and here are the conclusions. </div><div>It&#39;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&lt;T&gt;(_ 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&lt;T = Int64&gt;(_ 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&lt;T&gt;: 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&lt;T = Int64&gt;: 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(&quot;ops&quot;)</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&lt;T = P&gt;: 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&lt;P&gt;.</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&#39;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&lt;P&gt; 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&lt;R&gt; 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&lt;P&gt; = 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(&quot;123&quot;)</div><div>    </div><div> This is simple. With rule (II) we infer Storage&lt;String&gt;. However, if we do</div><div> </div><div>    let s: Storage = Storage(&quot;123&quot;)</div><div> </div><div> compiler must throw an error: cannot assign Storage&lt;String&gt; to Storage&lt;P&gt;.</div><div><br></div><div> Next, consider following type</div><div> </div><div>    struct Storage&lt;T = Int64&gt;: 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&lt;Int64&gt;.</div><div><br></div><div> What about generic function calls? Let&#39;s use the older example.</div><div> </div><div>    protocol P {}</div><div>    struct R: P {}</div><div><br></div><div>    struct Storage&lt;T = P&gt;: Integer {</div><div>        init(_ t: T)</div><div>    }</div><div><br></div><div>    func clear&lt;T&gt;(_ storage: Storage&lt;T&gt;)</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&#39;t have anything to infer</div><div> from so we should fill in the default. T = Storage&lt;P&gt;.</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&lt;T = R&gt;(_ storage: Storage&lt;T&gt;)</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&#39;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&#39;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">&lt;<a href="mailto:xiaodi.wu@gmail.com" target="_blank">xiaodi.wu@gmail.com</a>&gt;</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&#39;m afraid I don&#39;t understand your discussion. Can you simplify it for me by explaining your proposed solution in terms of Alexis&#39;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&lt;T=Int64&gt;(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&#39;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&lt;T&gt;(t: T) { ... }`, then `foo(22)` must infer `T` to be `Int`. That&#39;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&lt;T=Int64&gt;(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&#39;t IntegerLiteralConvertible</div><div><br></div><div>func bar&lt;T=Character&gt;(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">&lt;<a href="mailto:srdan.rasic@gmail.com" target="_blank">srdan.rasic@gmail.com</a>&gt;</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&#39;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 &quot;type declarations&quot;. Not sure what&#39;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) -&gt; 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&lt;Int&gt;` if it was defined as `struct X&lt;T = Int&gt;`. That&#39;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&#39;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&lt;Int&gt; = []</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&lt;T: BinaryInteger&gt;(_ input: BigInt&lt;T&gt;) -&gt; BigInt&lt;T&gt; { ... }</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&#39;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&lt;T: BinaryInteger&gt;(_ input: BigInt&lt;T&gt;) -&gt; BigInt&lt;T&gt; { ... }` </span><span style="font-size:12.800000190734863px">type is explicitly weakened or &quot;undefaulted&quot; 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&lt;Int64&gt;) -&gt; BigInt&lt;Int64&gt; { ... }</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&lt;Int64&gt;.</div><div><br></div><div>As for your last example, I guess we can&#39;t do anything about that and that&#39;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">&lt;<a href="mailto:abeingessner@apple.com" target="_blank">abeingessner@apple.com</a>&gt;</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&#39;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&lt;Int&gt; = []<br>
}<br>
<br>
<br>
which a consumer is using like:<br>
<br>
<br>
func process(_ input: BigInt) -&gt; BigInt { ... }<br>
let val1 = process(BigInt())<br>
let val2 = process(0)<br>
<br>
<br>
Ok that&#39;s all fairly straightforward. Now we decide that BigInt should expose its storage type for power-users:<br>
<br>
<br>
struct BigInt&lt;Storage: BinaryInteger = Int&gt;: Integer {<br>
  var storage: Array&lt;Storage&gt; = []<br>
}<br>
<br>
<br>
Let&#39;s make sure our consumer still works:<br>
<br>
<br>
func process(_ input: BigInt) -&gt; BigInt { ... }<br>
let val1 = process(BigInt())<br>
let val2 = process(0)<br>
<br>
<br>
Ok BigInt in process’s definition now means BigInt&lt;Int&gt;, 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&lt;T: BinaryInteger&gt;(_ input: BigInt&lt;T&gt;) -&gt; BigInt&lt;T&gt; { ... }<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&lt;Int&gt;(), and that will work. But for val2 this rule doesn&#39;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&lt;Int64&gt;) -&gt; BigInt&lt;Int64&gt; { ... }<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&lt;Int64&gt; is the correct result. If however we take stance that BigInt() means BigInt&lt;Int&gt;(), then we&#39;ll get a type checking error which our users will consider ridiculous: *of course* they wanted a BigInt&lt;Int64&gt; 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&lt;Int64&gt;, found BigInt&lt;Int&gt;<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>