Proposal: Allow explicit type parameter specification in generic function call

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@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@apple.com> wrote:

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

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.

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.

  - Doug

···

On Nov 30, 2016, at 4:09 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Mon Nov 28 2016, Douglas Gregor <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 21, 2016, at 3:05 PM, Ramiro Feria Purón via swift-evolution >> <swift-evolution@swift.org> wrote:

*I might even be willing to inhibit deduction,by default, of all generic
function type parameters that don't appear inthe parameter list.*

^ If this was adopted, what Dave is proposing is:

func f<T>() -> T // error: generic parameter T must be explicit
func f<explicit T>() -> T // ok

···

On Thu, 1 Dec 2016 at 11:17 Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

on Mon Nov 28 2016, Douglas Gregor <swift-evolution@swift.org> wrote:

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

Yeah, but IMO ideally we'd have a way to inhibit deduction of some
generic type parameters. I might even be willing to inhibit deduction,
by default, of all generic function type parameters that don't appear in
the parameter list.

--
-Dave

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

Basically, generic functions in Swift are implemented as functions taking
an additional parameter -- witness table, which contains implementation of
all functions in the protocol.
For example, the following:

func f<T: P>(a: T, b: T)

Turns into something like:

func f(a: Any, b: Any, witnessTableForT: UnsafePointer<_WitnessTable>)

Returning to my cited phrase, Swift specializes generic types (with
duplicated metadata; think C++ class specialization), but implements
generic functions without using specialization. One can say that a generic
type in Swift becomes a collection of distinct types, but a generic
function remains a single function. So we can't view them equally, and
current situation, where we can "explicitly specialize" types, but not
functions, is not an inconsistency.

I used word "semantically", because compiler can apply *specialization
optimization* to a function, which is usually followed by inlining. But
this optimization is guaranteed not to break the above assumptions.

···

2016-12-02 3:55 GMT+03:00 Ramiro Feria Purón <ramiro.feria.puron@gmail.com>:

*Unlike C++'s templates, a Swift's generic function is semantically a
single function.*

Anton, could you provide further insight on this?

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@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@swift.org> wrote:

On Nov 28, 2016, at 10:11 PM, Douglas Gregor via swift-evolution < swift-evolution@swift.org> wrote:

On Nov 21, 2016, at 3:05 PM, Ramiro Feria Purón via swift-evolution < swift-evolution@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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

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

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@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@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@apple.com> wrote:

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

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.

···

on Wed Nov 30 2016, Douglas Gregor <swift-evolution@swift.org> wrote:

On Nov 30, 2016, at 4:09 PM, Dave Abrahams via swift-evolution > <swift-evolution@swift.org> wrote:
on Mon Nov 28 2016, Douglas Gregor >> <swift-evolution@swift.org > >> <mailto:swift-evolution@swift.org>> >> wrote:

On Nov 21, 2016, at 3:05 PM, Ramiro Feria Purón via swift-evolution >>> <swift-evolution@swift.org> wrote:

--
-Dave

Anton,

Thanks for the explanation. Interesting!

I can understand the difference [I assume you are using the term template
specialization (developer code to provide a special implementation for
specific generic arguments) to refer to the term template instantiation
(compiler generated code for a set of actual generic arguments)]; however I
believe this is an implementation detail that should not influence
higher-level decisions unless it is strictly necessary. At the moment I
fail to see why it needs to be so.

Regards,
Ramiro

···

On Fri, 2 Dec 2016 at 22:34 Anton Zhilin <antonyzhilin@gmail.com> wrote:

2016-12-02 3:55 GMT+03:00 Ramiro Feria Purón <ramiro.feria.puron@gmail.com
>:

*Unlike C++'s templates, a Swift's generic function is semantically a
single function.*

Anton, could you provide further insight on this?

Basically, generic functions in Swift are implemented as functions taking
an additional parameter -- witness table, which contains implementation of
all functions in the protocol.
For example, the following:

func f<T: P>(a: T, b: T)

Turns into something like:

func f(a: Any, b: Any, witnessTableForT: UnsafePointer<_WitnessTable>)

Returning to my cited phrase, Swift specializes generic types (with
duplicated metadata; think C++ class specialization), but implements
generic functions without using specialization. One can say that a generic
type in Swift becomes a collection of distinct types, but a generic
function remains a single function. So we can't view them equally, and
current situation, where we can "explicitly specialize" types, but not
functions, is not an inconsistency.

I used word "semantically", because compiler can apply *specialization
optimization* to a function, which is usually followed by inlining. But
this optimization is guaranteed not to break the above assumptions.

But the fact that generics are implemented with witness tables is almost
entirely an implementation detail that doesn't poke through into the
programming model. So it's hard to understand why you're stressing this
difference here.

···

on Fri Dec 02 2016, Anton Zhilin <swift-evolution@swift.org> wrote:

2016-12-02 3:55 GMT+03:00 Ramiro Feria Purón <ramiro.feria.puron@gmail.com>:

*Unlike C++'s templates, a Swift's generic function is semantically a
single function.*

Anton, could you provide further insight on this?

Basically, generic functions in Swift are implemented as functions taking
an additional parameter -- witness table, which contains implementation of
all functions in the protocol.

For example, the following:

func f<T: P>(a: T, b: T)

Turns into something like:

func f(a: Any, b: Any, witnessTableForT: UnsafePointer<_WitnessTable>)

Returning to my cited phrase, Swift specializes generic types (with
duplicated metadata; think C++ class specialization), but implements
generic functions without using specialization. One can say that a generic
type in Swift becomes a collection of distinct types, but a generic
function remains a single function. So we can't view them equally, and
current situation, where we can "explicitly specialize" types, but not
functions, is not an inconsistency.

I used word "semantically", because compiler can apply *specialization
optimization* to a function, which is usually followed by inlining. But
this optimization is guaranteed not to break the above assumptions.

--
-Dave

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….

Adding the type information as a parameter effectively disables type inference for that parameter, because you have to pass the parameter explicitly. In some cases, like the unsafeBitCast function I pointed out, that is a good thing. In other cases, type inference might do the right thing for most callers, but specific call sites want the extra control provided by explicitly specifying generic arguments. 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.

plus, the syntax just seems too much like c++

Lots of languages use angle brackets like this; Swift has it for types already, so I find this an odd criticism.

  - Doug

···

On Nov 29, 2016, at 8:00 AM, Derrick Ho via swift-evolution <swift-evolution@swift.org> wrote:

On Tue, Nov 29, 2016 at 8:43 AM Goffredo Marocchi via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 28, 2016, at 10:11 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 21, 2016, at 3:05 PM, Ramiro Feria Purón via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

For example, here is a design pattern I would find clearer with explicit type information:

let cat = factory.get<Cat>()

vs

let cat: Cat = factory.get()

Having the explicit type information in angle brackets allows us to move the type information closer to where it make sense.

···

On 29 Nov 2016, at 23:52, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 29, 2016, at 8:00 AM, Derrick Ho via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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….

Adding the type information as a parameter effectively disables type inference for that parameter, because you have to pass the parameter explicitly. In some cases, like the unsafeBitCast function I pointed out, that is a good thing. In other cases, type inference might do the right thing for most callers, but specific call sites want the extra control provided by explicitly specifying generic arguments. 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.

plus, the syntax just seems too much like c++

Lots of languages use angle brackets like this; Swift has it for types already, so I find this an odd criticism.

  - Doug

On Tue, Nov 29, 2016 at 8:43 AM Goffredo Marocchi via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 28, 2016, at 10:11 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 21, 2016, at 3:05 PM, Ramiro Feria Purón via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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

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

···

On Nov 30, 2016, at 4:05 PM, Ramiro Feria Purón <ramiro.feria.puron@gmail.com> wrote:

Regards,
Ramiro

On Thu, 1 Dec 2016 at 01:36 Ramiro Feria Purón <ramiro.feria.puron@gmail.com <mailto:ramiro.feria.puron@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@gmail.com <mailto:ramiro.feria.puron@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@apple.com <mailto:dgregor@apple.com>> wrote:

On Nov 21, 2016, at 3:05 PM, Ramiro Feria Purón via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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

*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@apple.com> wrote:

On Nov 30, 2016, at 4:05 PM, Ramiro Feria Purón < > ramiro.feria.puron@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@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@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@apple.com> wrote:

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

For what it's worth, I believe the pull request Anton mentioned above (<https://github.com/apple/swift-evolution/pull/553&gt;\) addresses this. It provides both `Type<T>`, which means *exactly* T, and `AnyType<T>`, which means *any subtype of* `T`. You'd use `Type<T>` for type-pinning parameters and `AnyType<T>` for dynamically-typed parameters.

(`AnyType<T>` is a protocol-like type which all `Type<_>`s for subtypes of `T` "conform" to. Thus, you can pass a `Type<T>` as an `AnyType<T>`, but not vice versa.)

In other words, if `polymorphicSomething` were declared like:

   func polymorphicSomething<T>(_: AnyType<T>) {
     ...
   }

Then you would expect it to use the specific subtype you provided. But if you said:

   func polymorphicSomething<T>(_: Type<T>) {
     ...
   }

Then it would be clear in the signature that it was using only the static type of `T`, not the dynamic type. (It'd be clear because `Type<T>` can only contain `T`'s type instance, not subtypes' type instances.) Since `type(of:)` would return an `AnyType<Base>`, this line:

     polymorphicSomething(type(of: y))

Would be trying to pass `AnyType<Base>` to a `Type<_>` parameter, which would not fly. Thus, it would fail at compile time.

···

On Dec 1, 2016, at 2:09 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

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())

--
Brent Royal-Gordon
Architechies

For example, here is a design pattern I would find clearer with explicit type information:

let cat = factory.get<Cat>()

vs

let cat: Cat = factory.get()

Having the explicit type information in angle brackets allows us to move the type information closer to where it make sense.

Agreed :).

···

Sent from my iPhone

On 30 Nov 2016, at 06:40, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

On 29 Nov 2016, at 23:52, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 29, 2016, at 8:00 AM, Derrick Ho via swift-evolution <swift-evolution@swift.org> wrote:

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….

Adding the type information as a parameter effectively disables type inference for that parameter, because you have to pass the parameter explicitly. In some cases, like the unsafeBitCast function I pointed out, that is a good thing. In other cases, type inference might do the right thing for most callers, but specific call sites want the extra control provided by explicitly specifying generic arguments. 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.

plus, the syntax just seems too much like c++

Lots of languages use angle brackets like this; Swift has it for types already, so I find this an odd criticism.

  - Doug

On Tue, Nov 29, 2016 at 8:43 AM Goffredo Marocchi via swift-evolution <swift-evolution@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@swift.org> wrote:

On Nov 28, 2016, at 10:11 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 21, 2016, at 3:05 PM, Ramiro Feria Purón via swift-evolution <swift-evolution@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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

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

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

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

*For what it's worth, I believe the pull request Anton mentioned above
(<Refactor Metatypes by DevAndArtist · Pull Request #553 · apple/swift-evolution · GitHub
<https://github.com/apple/swift-evolution/pull/553&gt;&gt;\) addresses this.*

And this proposal is suggesting moving away from the pattern/practice,
rather than diving deeper into it. They are, therefore, (unintentionally)
antagonistic.

···

On Sun, 4 Dec 2016 at 21:56 Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

> On Dec 1, 2016, at 2:09 PM, Dave Abrahams via swift-evolution < > swift-evolution@swift.org> wrote:
>
> 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())

For what it's worth, I believe the pull request Anton mentioned above (<
https://github.com/apple/swift-evolution/pull/553&gt;\) addresses this. It
provides both `Type<T>`, which means *exactly* T, and `AnyType<T>`, which
means *any subtype of* `T`. You'd use `Type<T>` for type-pinning parameters
and `AnyType<T>` for dynamically-typed parameters.

(`AnyType<T>` is a protocol-like type which all `Type<_>`s for subtypes of
`T` "conform" to. Thus, you can pass a `Type<T>` as an `AnyType<T>`, but
not vice versa.)

In other words, if `polymorphicSomething` were declared like:

   func polymorphicSomething<T>(_: AnyType<T>) {
     ...
   }

Then you would expect it to use the specific subtype you provided. But if
you said:

   func polymorphicSomething<T>(_: Type<T>) {
     ...
   }

Then it would be clear in the signature that it was using only the static
type of `T`, not the dynamic type. (It'd be clear because `Type<T>` can
only contain `T`'s type instance, not subtypes' type instances.) Since
`type(of:)` would return an `AnyType<Base>`, this line:

     polymorphicSomething(type(of: y))

Would be trying to pass `AnyType<Base>` to a `Type<_>` parameter, which
would not fly. Thus, it would fail at compile time.

--
Brent Royal-Gordon
Architechies

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

I care about the use-site far more than the signature. When you pass
something in angle brackets, the static type is very clearly what
matters.

···

on Sun Dec 04 2016, Brent Royal-Gordon <brent-AT-architechies.com> wrote:

On Dec 1, 2016, at 2:09 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

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())

For what it's worth, I believe the pull request Anton mentioned above
(<https://github.com/apple/swift-evolution/pull/553&gt;\) addresses this. It provides both `Type<T>`,
which means *exactly* T, and `AnyType<T>`, which means *any subtype of* `T`. You'd use `Type<T>` for
type-pinning parameters and `AnyType<T>` for dynamically-typed parameters.

(`AnyType<T>` is a protocol-like type which all `Type<_>`s for subtypes of `T` "conform" to. Thus,
you can pass a `Type<T>` as an `AnyType<T>`, but not vice versa.)

In other words, if `polymorphicSomething` were declared like:

   func polymorphicSomething<T>(_: AnyType<T>) {
     ...
   }

Then you would expect it to use the specific subtype you provided. But if you said:

   func polymorphicSomething<T>(_: Type<T>) {
     ...
   }

Then it would be clear in the signature that it was using only the static type of `T`, not the
dynamic type.

--
-Dave