on Wed Nov 30 2016, Douglas Gregor <[email protected]> wrote:
>> On Nov 30, 2016, at 4:09 PM, Dave Abrahams via swift-evolution > <[email protected]> wrote: >> >> >> on Mon Nov 28 2016, Douglas Gregor >> <[email protected] > >> <mailto:[email protected]>> >> wrote: >> >>>> 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. >> >> Yeah, but IMO ideally we'd have a way to inhibit deduction of some >> generic type parameters. > > Well, I’d say we already have it: it’s the pass-a-metatype approach > already used by unsafeBitCast, and I think usages of that API read > really, really well as it is. IMO they are marred by “.self,” but that's really a minor issue. More importantly, they suggest that the metatype argument will be used in some dynamic way (e.g. by calling a static method or an init), instead of just as a way to get the right type inference. In some cases that can make a dramatic difference in the resulting semantics. func polymorphicSomething<T>(_: T.Type) { ... } class Base {} class Derived : Base {} func otherThing(x: Base) { // Surprise! I'm going to ignore the dynamic type you gave me and // just use Base polymorphicSomething(type(of: y)) } otherThing(Derived()) This is exactly why we have MemoryLayout<T>.size and not memoryLayout(Foo.self).size >> I might even be willing to inhibit deduction, >> by default, of all generic function type parameters that don't appear in >> the parameter list. > > I’m not a fan of this for the reasons I and Ramiro outlined. Details please? AFAICT Ramiro outlined arguments *for* this approach and you merely referred to your thread with me in explaining why you don't like it. -- -Dave _______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
