> On Nov 21, 2016, at 3:05 PM, Ramiro Feria Purón via swift-evolution 
> <[email protected]> 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.

I’d like one bit of clarification in the proposal. Right now, one is not 
permitted to have a type parameter in a generic function that isn’t used 
somewhere in its signature, e.g.,

        func f<T>() -> Void { … }   // error: T is not part of the signature of 
f()

This restriction is obvious in today’s Swift, because there is absolutely no 
way one could ever use this function. With your proposed extension, it would be 
possible to use this function. Does the restriction remain or is it lifted?

Personally, I’d like the restriction to stay, because it feels like such 
functions fall into the same camp as unsafeBitCast: if the type parameter 
affects how the function operates but is *not* part of its signature, then it 
should be expressed like a normal parameter (of a metatype). It also helps 
provide better diagnostics when changing a generic function to no longer 
require one of its type parameters.

And, as Dave notes, it’s effectively syntactic sugar, so it belongs in Swift 4 
stage 2.

        - Doug


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

Reply via email to