> On Jan 24, 2017, at 11:41, Alexis via swift-evolution 
> <[email protected]> wrote:
> 
> It’s worth noting that the question of “how do these defaults interact with 
> other defaults” is an issue that has left this feature dead in the water in 
> the Rust language despite being accepted for inclusion two years ago. See 
> https://internals.rust-lang.org/t/interaction-of-user-defined-and-integral-fallbacks-with-inference/2496
>  for some discussion of the issues at hand.
> 
> For those who don’t want to click that link, or are having trouble 
> translating the syntax/terms to Swift. The heart of Niko’s post is the 
> following (note: functions are used here for expedience; you can imagine 
> these are `inits` for a generic type if you wish):
> 
> // Example 1: user supplied default is IntegerLiteralConvertible
> 
> func foo<T=Int64>(t: T) { ... }
> 
> foo<_>(22)
> //  ^
> //  |
> //  What type gets inferred here?
> 
> 
> 
> // Example 2: user supplied default isn't IntegerLiteralConvertible
> 
> func bar<T=Character>(t: T) { ... }
> 
> bar<_>(22)
> //  ^
> //  |
> //  What type gets inferred here?
> 
> 
> There are 4 strategies:
> 
> (Note: I use “integer literal” here for simplicity; in general it's “any kind 
> of literal, and its associated LiteralType”. So this reasoning also applies 
> to FloatLiteralType, StringLiteralType, BooleanLiteralType, etc.)
> 
> * Unify all: always unify the variables with all defaults. This is the 
> conservative choice in that it gives an error if there is any doubt.
> 
> * Prefer literal: always prefer IntegerLiteralType (Int). This is the 
> maximally backwards compatible choice, but I think it leads to very 
> surprising outcomes.
> 
> * Prefer user: always the user-defined choice. This is simple from one point 
> of view, but does lead to a potentially counterintuitive result for example 2.
> 
> * Do What I Mean (DWIM): Prefer the user-defined default, except in the case 
> where the variable is unified with an integer literal and the user-defined 
> default isn't IntegerLiteralConvertible. This is complex to say but leads to 
> sensible results on both examples. (Basically: prefer user, but fallback to 
> IntegerLiteralType if the user default doesn’t actually make sense)
> 
> | Strategy       | Example 1 | Example 2 |
> | -------------- | --------- | --------- |
> | Unify all      | Error     | Error     |
> | Prefer literal | Int       | Int       |
> | Prefer user    | Int64     | Error     |
> | DWIM           | Int64     | Int       |
> 
> Personally, I’ve always favoured DWIM. Especially in Swift where 
> IntegerLiteralType inference is so frequently used (you don’t want adding a 
> default to cause code to stop compiling!). In practice I don’t expect there 
> to be many cases where this ambiguity actually kicks in, as it requires the 
> user-specified default to be a LiteralConvertible type that isn't the 
> relevant LiteralType, and for the type variable to affect an actual Literal. 
> So <T=String>(x: T) never causes problems, but <T=StaticString>(x: T) does.
> 
> As for the matter of “what if I want the other one” — you clearly know the 
> actual type you want; just name it explicitly.

We could just remove that parameter from the type inference system... if the 
default value is Int and the user passes in a String, that'd be an error, 
unless the user also sets that parameter to String.

I'd envisioned using default parameters as more "compile-time configuration 
options" than something for the type system to actually have deal with. IIRC, I 
was trying to come up with a way to write just one arbitrary-sized integer 
struct where I *could* specify the width of its internal calculations, but 
would usually just use a default value.

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

Reply via email to