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

Derrick Ho wh1pch81n at gmail.com
Tue Nov 29 10:00:32 CST 2016


I don't think the angle brackets adds any additional benefit than adding
the type information as a parameter. Adding Angle brackets will just make
it more crowded....plus, the syntax just seems too much like c++

On Tue, Nov 29, 2016 at 8:43 AM Goffredo Marocchi via swift-evolution <
swift-evolution at swift.org> wrote:

I think this is a case where the angle bran jets is both more readable and
terse without losing context... opinions and all :).

Sent from my iPhone

On 29 Nov 2016, at 09:47, Andrew Trick via swift-evolution <
swift-evolution at swift.org> wrote:


On Nov 28, 2016, at 10:11 PM, Douglas Gregor via swift-evolution <
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…


Angle brackets in function calls are hideous. This is objectively more
clear and much prettier IMO:

  unsafeBitCast(something, to: Int)

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.


+1 for required type parameters being normal parameters.

I think the case mentioned in the proposal reads much better as:

  processAll(in: vehicles, as: Bicycle, condition: aboveSpeedLimit)

If angle brackets can be limited to generic definitions and type names,
that’s a great accomplishment.

-Andy


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

- Doug


_______________________________________________
swift-evolution mailing list
swift-evolution at swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


_______________________________________________
swift-evolution mailing list
swift-evolution at swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution at swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20161129/fbe77484/attachment.html>


More information about the swift-evolution mailing list