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

Ramiro Feria Purón ramiro.feria.puron at gmail.com
Wed Nov 30 18:05:09 CST 2016


Formally, perhaps the proposal should include the following:

1. Missing type parameters are assumed to be implicit and trailing (as in
the particular case of all type parameters missing).

2. _ is allowed as a placeholder when specifying explicit parameters in a
function call. The corresponding parameter is assumed to be implicit.

In addition to this, and following the discussion on the unsafeBitCast example,
it might be a good idea to incorporate to the proposal adding an (optional)
explicit keyword for type parameters in generic function definitions:

func unsafeBitCast<explicit U, T>(_ x: T) -> U


foo(unsafeBitCast(something))      // error: explicit type parameter
required

foo(unsafeBitCast<U>(something))   // ok



Regards,

Ramiro





On Thu, 1 Dec 2016 at 01:36 Ramiro Feria Purón <ramiro.feria.puron at gmail.com>
wrote:

> Or perhaps in a more "swifty" way, use _ for implicit type parameters,
> i.e.:
>
> f<_,T>()
>
> On Thu, 1 Dec 2016 at 01:13 Ramiro Feria Purón <
> ramiro.feria.puron at gmail.com> wrote:
>
> Douglas,
>
> Regarding the question on the restriction for type parameters to appear on
> the signature, the answer is remain. The proposal does not intend this
> restriction to be lifted.
>
> One might expect to find a few legitimate cases where having it lifted
> would be handy or desirable. They seem to emerge often, for example,
> while developing components using Core Data. From a real case scenario:
>
> protocol Activable { var dateLastActive: Date { get } }
>
>
> class A: NSManagedObject {}
>
> class B: NSManagedObject {}
>
> //..
>
> class Z: NSManagedObject {}
>
>
> extension A: Activable { var dateLastActive: Date { return Date() } }
>
> extension B: Activable { var dateLastActive: Date { return Date() } }
>
> //..
>
> extension Z: Activable { var dateLastActive: Date { return Date() } }
>
>
>
>
> func deleteInactiveObjects<T: NSManagedObject>(since date: Date,
> inContext context: NSManagedObjectContext) where T: Activable {
>
>     //..
>
> }
>
>
> // for the sake of the example
>
> let context = NSManagedObjectContext(concurrencyType: .
> privateQueueConcurrencyType)
>
> let yesterday = Date()
>
> let oneWeekAgo = Date()
>
>
>
> deleteInactiveObjects<A>(since: yesterday, inContext: context)
>
> deleteInactiveObjects<B>(since: oneWeekAgo, inContext: context)
>
> //..
>
> (here again, as you mention, the parameter affects how the function
> operates, yet it is not part of the signature)
>
> If the restriction was lifted, however, it would also be in detriment of
> the educational value of the proposal (apart from your arguments above). When
> defining a generic function, it feels natural to expect all of the type
> parameters to be present in the generic function signature. Relaxing this
> rule could be perceived by the novice as an invitation to an obscure
> design. In the best case, it would generate doubts about its actual intent.
>
> From a pedagogical perspective, the proposal aims to save Swift from
> disappointment when this topic is brought to discussion, say whether at the
> end of a Programming or a Compilers undergraduate course -- albeit fully
> understanding (or implementing) the current alternatives could be an
> excellent exercise for the class.
>
> From the (primary) language and development perspective, I don't think it
> could be expressed in a better way than in your lines, which are truly
> appreciated:
>
> "*That’s how I see this proposal: not as a replacement for the metatype
> parameter idiom that unsafeBitCast uses, but as a way to be more explicit
> at particular call sites when type inference either fails (e.g., due to
> lack of contextual type information), produces a result different than what
> is desired, or is sufficiently complicated that the call site requires more
> documentation.*"
>
> Finally, we should also consider the possibility of being explicit about
> some but not all type parameters. If allowed, something like "only trailing
> type parameters could be missing" would be necessary to avoid ambiguity.
>
> All the best,
> Ramiro
>
>
> On Tue, 29 Nov 2016 at 17:11 Douglas Gregor <dgregor at apple.com> 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.
>
> 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
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20161201/7a368371/attachment.html>


More information about the swift-evolution mailing list