[swift-evolution] Default Generic Arguments

David Sweeris davesweeris at mac.com
Tue Jan 24 15:16:17 CST 2017


> On Jan 24, 2017, at 11:41, Alexis via swift-evolution <swift-evolution at swift.org> 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170124/ade983c1/attachment.html>


More information about the swift-evolution mailing list