I would like to pitch an idea of allowing the programmer to optionally inform the compiler of a function parameter's type at call site, instead of always having the compiler infer the type.
Motivation
Overloaded functions are common in Swift. All of them rely on the compiler to infer at call site. Some times, there is no ambiguity between overloaded functions, and the compiler can always reliably infer which function is the correct one. For example:
// function 1
func foo(bar: Int) {}
// function 2
func foo(bar: String) {}
foo(bar: 42) // calls function 1
foo(bar: "42") // calls function 2
There is no ambiguity between function 1 and function 2, because an integer won't be mistaken as a string, neither the vice versa. foo(bar: 42)
always calls function 1, and foo(bar: "42")
always calls function 2. However, if there is a 3rd function that introduces ambiguity:
// function 3
func foo<T: StringProtocol>(bar: T) {}
foo(bar: "42") // still calls function 2
foo(bar: "42")
still always calls function 2, never function 3. Although "42" conforms to StringProtocol
, the compiler prefers concrete types, so it always chooses function 2 over function 3 when both are suitable candidates. There is no way of telling the compiler that it should choose function 3, but at least the preference is clear enough that the compiler can resolve the ambiguity, and the programmer can be certain that function 2 is called.
Now, consider the following 2 functions:
// function 4
func foo<T: Hashable>(bar: T) {}
// function 5
func foo<T: Comparable>(bar: T) {}
foo(bar: 3.14) // Error: Ambiguous use of 'foo(bar:)'
Both function 4 and function 5 are suitable candidates for foo(bar: 3.14)
, but unlike the ambiguity between function 2 and function 3, the compiler doesn't know which one to call. In addition, since both Hashable
and Comparable
can only be used as a generic constraint, you can't do something like this work around it:
foo(bar: 3.14 as Hashable) // Error: Protocol 'Hashable' can only be
// used as a generic constraint because it
// has Self or associated type requirements
foo(bar: 3.14 as Comparable) // Error: Protocol 'Comparable' can only be
// used as a generic constraint because it
// has Self or associated type requirements
Proposed Solution:
I propose allowing the programmer to optionally inform the compiler which function to call. Syntactically, I have 3 potential designs in mind:
// Design 1: Overload the "as" keyword.
// When "as SomeType" is used at the end of a parameter value in
// an overloaded function, the compiler doesn't cast the value to
// SomeType. Instead, it looks among the overloads for a function
// where the parameter is of SomeType. If such a function exists,
// it will be chosen by the compiler as the correct one to call,
// and the parameter value will be cast to SomeType if necessary.
foo(bar: 3.14 as Comparable) // calls function 5
// Design 2: Use the ":" notation.
// This design probably will create additional ambiguity for
// parameters without labels.
foo(bar: 3.14: Comparable) // calls function 5
// Design 3: Use C-like typecasting notation.
foo(bar: (Comparable) 3.14) // calls function 5
Additional Benefits
By allowing the programmer to choose which overloaded function to use, the compiler's preference for concrete types can be overridden. For example, a concrete function will be able to call its generic counterpart in its body. This can result in better code reuse in protocol-oriented programming.