[swift-evolution] Proposal: Allow explicit type parameter specification in generic function call

Dave Abrahams dabrahams at apple.com
Wed Nov 30 18:09:11 CST 2016


on Mon Nov 28 2016, Douglas Gregor <swift-evolution at swift.org> wrote:

>> On Nov 21, 2016, at 3:05 PM, Ramiro Feria Purón via swift-evolution
> <swift-evolution at swift.org> wrote:
>> 
>> Problem:
>> 
>> Currently, it is not possible to be explicit about the generic parameters (type parameters) in a
> generic function call. Type parameters are inferred from actual parameters:
>
>> 
>> func f<T>(_ t: T) {
>>     
>>     //..
>> }
>> 
>> f(5)            // T inferred to be Int
>> f("xzcvzxcvx")  // T inferred to be string 
>> 
>> If no type parameter is involved in the formal parameters, the type parameter needs to be used somehow as part of the return type. For example:
>> 
>> func g<T>(_ x: Int) -> [T] {
>>     
>>     var result: [T] = []
>>     
>>     //..
>>     
>>     return result
>> }
>> 
>> In such cases, the type parameters must be inferrable from the context:
>> 
>> g(7)                            // Error: T cannot be inferred
>> let array = g(7)                // Error: T cannot be inferred
>> let array: [String] = g(7)      // Ok: T inferred to be String
>> let array = g<String>(7)        // Error: Cannot explicitly specialise generic function
>> 
>> 
>> 
>> Proposed Solution:
>> 
>> Allow explicit type parameters in generic function call:
>> 
>> let _ = g<String>(7)            // Ok
>> 
>> 
>> 
>> Motivation:
>> 
>> Consider the following contrived example:
>> 
>> class Vehicle {
>>     var currentSpeed = 0
>>     //..
>> }
>> 
>> class Bicycle: Vehicle {
>>     //..
>> }
>> 
>> class Car: Vehicle {
>>     //..
>> }
>> 
>> @discardableResult
>> func processAll<T: Vehicle>(in vehicles: [Vehicle], condition: (Vehicle) -> Bool) -> [T] {
>>     
>>     var processed: [T] = []
>>     
>>     for vehicle in vehicles {
>>         guard let t = vehicle as? T, condition(vehicle) else { continue }
>>         //..
>>         processed.append(t)
>>     }
>>     
>>     return processed
>> }
>> 
>> func aboveSpeedLimit(vehicle: Vehicle) -> Bool {
>>     return vehicle.currentSpeed >= 100
>> }
>> 
>> 
>> let processedVehicles = processAll(in: vehicles, condition: aboveSpeedLimit) // Uh, T inferred to
> be Vehicle!
>> let processedCars: [Car] = processAll(in: vehicles, condition: aboveSpeedLimit) // T inferred to
> be Car
>> processAll<Bicycle>(in: vehicles, condition: aboveSpeedLimit) // This should be allowed under this
> proposal
>> 
>> 
>> Notes:
>> 
>> If necessary, the (real life) Swift code that lead to the proposal could be shared.
>
> This seems completely reasonable to me. I had always expected us to
> implement this feature, but we never got around to it, and it wasn’t a
> high priority because one can always use type inference. Additionally,
> there were a few places where we originally thought we wanted this
> feature, but prefer the more-explicit form where the user is required
> to explicitly pass along a metatype. unsafeBitCast is one such case:
>
> 	func unsafeBitCast<T, U>(_ x: T, to: U.Type) -> U
>
> Even if we had the ability to provide explicit type arguments, we
> would *not* want to change this signature to
>
> 	func unsafeBitCast<U, T>(_ x: T) -> U     // bad idea
>
> because while it makes the correct usage slightly cleaner:
>
> 	unsafeBitCast<Int>(something)	// slightly prettier, but...
>
> it would enable type inference to go wild with unsafe casts:
>
> 	foo(unsafeBitCast(something))	// just cast it to.. whatever	
>
> which is… not great.

Yeah, but IMO ideally we'd have a way to inhibit deduction of some
generic type parameters.  I might even be willing to inhibit deduction,
by default, of all generic function type parameters that don't appear in
the parameter list.

-- 
-Dave



More information about the swift-evolution mailing list