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

Ramiro Feria Purón ramiro.feria.puron at gmail.com
Thu Dec 1 17:26:52 CST 2016


*Yeah, this makes sense. Presumably this means that one could also write*

* foo<>(1)*

*where everything is still deduced. In C++, this syntax exists and means
that non-template functions named “foo” are ignored.*

Yes, it means so. One could have:

foo()

foo<>()         // redundant

foo<_>()        // redundant

foo<T>()

foo<_, _>()     // redundant

foo<T, _>()     // redundant

foo<_, U>()

foo<T, U>()

//..

One historical reason to allow redundant cases has been to ease automatic
code generation.

*This could be a more general feature that works wherever a type is
specified and deduction works. For example, today you can do, e.g,*

* let d1: Dictionary = [ “Hello” : 2017 ]*

*and we’ll infer the type arguments. It wouldn’t be unreasonable to allow *

* let d2: Dictionary<_, Int32> = [ “Hello”: 2017 ]*

*or*

* let d3: [ _: Int32 ] = [ “Hello”: 2017 ]*

*or *

* let d4 = [ “Hello”: 2017 ] as [ _: Int32 ]*

*to partially specify the types of the result. I think this is a
separate-but-interesting proposal (that is out of scope for Swift 4 stage
1).*

Yes, yes, yes, yes and yes. Yes! A very rewarding outcome from the
discussion! We should start a co-authored proposal asap.

*As I noted in my reply to Dave, I personally don’t want this: I feel that
the idiom used by unsafeBitCast produces more readable code with
already-existing features.*

In this aspect I disagree. The argument is the following:

One of the problems generic functions as a language feature aim to assist
with (eradicate) is the questionable pattern of using formal arguments in
ordinary functions for the *sole* purpose of passing type information. It
is not only about duplicated code. And the problem with the pattern is that
there is nothing that tells the compiler and other code analysing tools
that the parameter has been used only for that purpose. Nor the human eye
without using additional knowledge.Thus, when that pattern is used in a
generic function, it is like having the antithesis of the feature while
using the feature itself. And this is a reason why I believe the discussion
goes beyond a syntactic convenience.

Emphasising on the proposed explicit keyword above, this would be a very
convenient way to pass in formation to the compiler (and the user) that
would be otherwise hosted by the author of the function (as it is the case
of the unsafeBitCast example).

Best regards,
Ramiro


On Thu, 1 Dec 2016 at 15:53 Douglas Gregor <dgregor at apple.com> wrote:

> On Nov 30, 2016, at 4:05 PM, Ramiro Feria Purón <
> ramiro.feria.puron at gmail.com> wrote:
>
> 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).
>
>
> Yeah, this makes sense. Presumably this means that one could also write
>
> foo<>(1)
>
> where everything is still deduced. In C++, this syntax exists and means
> that non-template functions named “foo” are ignored.
>
> 2. _ is allowed as a placeholder when specifying explicit parameters in a
> function call. The corresponding parameter is assumed to be implicit.
>
>
> This could be a more general feature that works wherever a type is
> specified and deduction works. For example, today you can do, e.g,
>
> let d1: Dictionary = [ “Hello” : 2017 ]
>
> and we’ll infer the type arguments. It wouldn’t be unreasonable to allow
>
> let d2: Dictionary<_, Int32> = [ “Hello”: 2017 ]
>
> or
>
> let d3: [ _: Int32 ] = [ “Hello”: 2017 ]
>
> or
>
> let d4 = [ “Hello”: 2017 ] as [ _: Int32 ]
>
> to partially specify the types of the result. I think this is a
> separate-but-interesting proposal (that is out of scope for Swift 4 stage
> 1).
>
>
> 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
>
>
> As I noted in my reply to Dave, I personally don’t want this: I feel that
> the idiom used by unsafeBitCast produces more readable code with
> already-existing features.
>
> - Doug
>
>
> 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/5f984f9b/attachment.html>


More information about the swift-evolution mailing list