[Review] SE-0057: Importing Objective-C Lightweight Generics

Hello Swift community,

The review of "Importing Objective-C Lightweight Generics" begins now and runs through April 5. The proposal is available here:

  swift-evolution/0057-importing-objc-generics.md at master · apple/swift-evolution · GitHub

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at:
  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  * What is your evaluation of the proposal?
  * Is the problem being addressed significant enough to warrant a change to Swift?
  * Does this proposal fit well with the feel and direction of Swift?
  * If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at

  swift-evolution/process.md at master · apple/swift-evolution · GitHub

Thank you,

-Chris Lattner
Review Manager

Responses inline!

Sincerely,
Zachary Waldowski
zach@waldowski.me

  * What is your evaluation of the proposal?

The strongest of +1's. It will allow much greater flexibility for the
bridge, both in our own code and Apple's frameworks.

  * Is the problem being addressed significant enough to warrant a change to Swift?

Yes. Like SE-0055, it's mildly ironic that these new features can
occasionally be more expressive or compiler-checked by Clang than by
Swift.

  * Does this proposal fit well with the feel and direction of Swift?

At the high-level, absolutely. More generics = safer and smarter code.

At the lower-level, I'm somewhat concerned about the mismatch between
the Swift side of lightweight generics vs. honest-to-god Swift generics.
As an instructor, I worry that the limitations may make learning
generics even more hairy. Considering "Restrictions on uses of
Objective-C parameterized classes" (while all the restrictions
completely make sense), I wonder how difficult it'll be to explain why
in a given specific instance (i.e., pulling a value out of a [String:
AnyObject]) can't be casted.

  * If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I've used type-erased generics before in other languages. While not
preferable when you have something like Swift's implementation, this is
acceptable for a bridge.

  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

I've been considering the effects of this feature since bridging came to
ObjC, really excited to see it. Quick glance at the proposal itself.

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

A couple of questions, since this proposal hasn't had the regular
Evolution run-through:

1. The section "Importing unspecialized types" makes reference to
defaulting to the unbound requirement when something isn't annotated in
Objective-C. This is notably something that isn't true in Swift at
present (i.e., a T: ErrorType can't be satisfied by ErrorType). Would
this be a change that comes generalized to Swift, or is just true of the
lightweight generics bridge.

2. How undefined is the undefined behavior in things coming from
Objective-C? If something is exposed into Swift as one type bounds, but
for legacy or bug reasons, another class is pushed across the bridge,
precisely what color will the resulting explosion be at runtime?

3. Re: "Opting in to type argument discovery", how would initialization
look for GKComponentSystem? Having to do
GKComponentSystem<SomeType>(componentClass: SomeType.self) would be
pretty onerous. Would this have to be treated in the overlay framework
too as well?

···

On Thu, Mar 31, 2016, at 02:11 PM, Chris Lattner via swift-evolution wrote:

  * What is your evaluation of the proposal?

I like it, but I have a couple of minor issues with the details.

The first is that type discovery does not support the full richness of either language's type system. Both languages allow a type to be either a class or a group of protocols, and Objective-C even allows one class + N protocols. I believe these methods should permit any valid Objective-C generic type to be expressed. That could be achieved by having two methods (plus class method variants):

  - (nullable Class)classForGenericArgumentAtIndex:(NSUInteger)index; // nil means id
  - (NSArray<Protocol*>*)protocolsForGenericArgumentAtIndex:(NSUInteger)index;

The second is the issue of extensions. Rather than rejecting the use of the generic type parameter, I think we would be better off treating it as though it were a nested typealias. That is, when you write this:

  @interface MySet<__covariant ObjectType: id<NSCopying>> : NSObject

Then MySet.ObjectType is NSCopying. That would allow you to use ObjectType in extensions without particularly caring whether or not you were working with full generics.

The third is that I'm troubled by the invisibility of this feature given its limitations. I understand why you don't want to implement full type erasure, but there should at least be some suggestion of the semantics in the generated headers:

  class MySet<@erased ObjectType: NSCopying>: NSObject

@erased does not have to actually work in user-written code—it's just there in the generated header to mark the type parameter's special semantics.

Finally, this is a severable issue, but I think it's worth mentioning: it would be very helpful to expose generic classes back to Objective-C somehow. One of my projects has a CaseInsensitiveDictionary type written in Swift; I have reference-typed NSObject subclasses to expose it to Objective-C, but these can't be generic. Even lightweight generics, even exposed only to Objective-C, would make this code a lot better.

  * Is the problem being addressed significant enough to warrant a change to Swift?

Yes. I've missed this type information in Swift.

  * Does this proposal fit well with the feel and direction of Swift?

Yes.

  * If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Swift's interop concerns are pretty unique, in my experience.

  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Well, I've been drafting this email for several hours. Does that count?

···

--
Brent Royal-Gordon
Architechies

A few quick questions:

* Is there a swift-evolution discussion thread? It's marked as TODO
currently.
* What if you want your generic parameter to be a protocol instead of a
class?

Thanks!
Andrew Bennett

···

On Friday, 1 April 2016, Chris Lattner via swift-evolution < swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "Importing Objective-C Lightweight Generics" begins now and
runs through April 5. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0057-importing-objc-generics.md

Reviews are an important part of the Swift evolution process. All reviews
should be sent to the swift-evolution mailing list at:
        https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the
review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review
through constructive criticism and, eventually, determine the direction of
Swift. When writing your review, here are some questions you might want to
answer in your review:

        * What is your evaluation of the proposal?
        * Is the problem being addressed significant enough to warrant a
change to Swift?
        * Does this proposal fit well with the feel and direction of Swift?
        * If you have you used other languages or libraries with a similar
feature, how do you feel that this proposal compares to those?
        * How much effort did you put into your review? A glance, a quick
reading, or an in-depth study?

More information about the Swift evolution process is available at

        https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris Lattner
Review Manager

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

  * What is your evaluation of the proposal?

Generally I like the proposal and I think it helps solidify the bridging story.

On casting: In our coding standards, any use of “as!” warrants much higher scrutiny (any use of try! or IUOs gets the same treatment). For a type that opts-in to type discovery, why can’t “as?” casting work? It seems like there’s enough information to determine if the cast should succeed or not.

It is also slightly strange that the collection types have the same problems but allow bridging, and in the as! case they actually defer checking until later. It may just be an inconsistency we have to live with for practical reasons.

  * Is the problem being addressed significant enough to warrant a change to Swift?

Yes; the mountains of Objective-C code aren’t going away anytime soon.

  * Does this proposal fit well with the feel and direction of Swift?

It has some rough edges but I think they’re unavoidable given the fundamental differences between Objective-C and Swift generics.

Russ

A few quick questions:

* Is there a swift-evolution discussion thread? It's marked as TODO currently.

Thanks for catching that. The thread is here:

  http://thread.gmane.org/gmane.comp.lang.swift.evolution/2886

and I’ve updated the proposal with a link.

* What if you want your generic parameter to be a protocol instead of a class?

The generic parameter itself can be required to conform to @objc protocols; the generic argument will be able to be an @objc protocol when the protocol conforms to itself.

  - Doug

···

On Mar 31, 2016, at 2:40 PM, Andrew Bennett via swift-evolution <swift-evolution@swift.org> wrote:

Thanks!
Andrew Bennett

On Friday, 1 April 2016, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hello Swift community,

The review of "Importing Objective-C Lightweight Generics" begins now and runs through April 5. The proposal is available here:

        https://github.com/apple/swift-evolution/blob/master/proposals/0057-importing-objc-generics.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at:
        https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

        * What is your evaluation of the proposal?
        * Is the problem being addressed significant enough to warrant a change to Swift?
        * Does this proposal fit well with the feel and direction of Swift?
        * If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
        * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at

        https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris Lattner
Review Manager

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

Responses inline!

Sincerely,
  Zachary Waldowski
  zach@waldowski.me

  * What is your evaluation of the proposal?

The strongest of +1's. It will allow much greater flexibility for the
bridge, both in our own code and Apple's frameworks.

  * Is the problem being addressed significant enough to warrant a change to Swift?

Yes. Like SE-0055, it's mildly ironic that these new features can
occasionally be more expressive or compiler-checked by Clang than by
Swift.

  * Does this proposal fit well with the feel and direction of Swift?

At the high-level, absolutely. More generics = safer and smarter code.

At the lower-level, I'm somewhat concerned about the mismatch between
the Swift side of lightweight generics vs. honest-to-god Swift generics.
As an instructor, I worry that the limitations may make learning
generics even more hairy. Considering "Restrictions on uses of
Objective-C parameterized classes" (while all the restrictions
completely make sense), I wonder how difficult it'll be to explain why
in a given specific instance (i.e., pulling a value out of a [String:
AnyObject]) can't be casted.

I agree that the restrictions will feel arbitrary, because it takes a fairly deep understanding of the implementation models of both ObjC generics and Swift generics to understand why they are there. My hope is that imported ObjC generic classes will be used far more than they are extended.

_

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

A couple of questions, since this proposal hasn't had the regular
Evolution run-through:

It did get some discussion at

  http://thread.gmane.org/gmane.comp.lang.swift.evolution/2886

1. The section "Importing unspecialized types" makes reference to
defaulting to the unbound requirement when something isn't annotated in
Objective-C. This is notably something that isn't true in Swift at
present (i.e., a T: ErrorType can't be satisfied by ErrorType). Would
this be a change that comes generalized to Swift, or is just true of the
lightweight generics bridge.

@objc protocols don’t have associated types or self requirements, and (from an implementation standpoint) a object of @objc protocol type isn’t actually any different from an object of some class type that conforms to the @objc protocol.

2. How undefined is the undefined behavior in things coming from
Objective-C? If something is exposed into Swift as one type bounds, but
for legacy or bug reasons, another class is pushed across the bridge,
precisely what color will the resulting explosion be at runtime?

It’s truly undefined behavior. For example, if it’s a Swift-defined class we’re expecting but we get an object of some unrelated type in its place, we might very well poke directly at storage in the object (i.e., when accessing a stored property), or grab a random pointer out of the vtable that isn’t what we expected.

3. Re: "Opting in to type argument discovery", how would initialization
look for GKComponentSystem? Having to do
GKComponentSystem<SomeType>(componentClass: SomeType.self) would be
pretty onerous.

That onerous solution is what one gets from this proposal.

Would this have to be treated in the overlay framework
too as well?

One would have to clean this up in the overlay.

  - Doug

···

On Mar 31, 2016, at 11:42 AM, Zach Waldowski via swift-evolution <swift-evolution@swift.org> wrote:
On Thu, Mar 31, 2016, at 02:11 PM, Chris Lattner via swift-evolution > wrote:

  * What is your evaluation of the proposal?

I like it, but I have a couple of minor issues with the details.

The first is that type discovery does not support the full richness of either language's type system. Both languages allow a type to be either a class or a group of protocols, and Objective-C even allows one class + N protocols. I believe these methods should permit any valid Objective-C generic type to be expressed. That could be achieved by having two methods (plus class method variants):

  - (nullable Class)classForGenericArgumentAtIndex:(NSUInteger)index; // nil means id
  - (NSArray<Protocol*>*)protocolsForGenericArgumentAtIndex:(NSUInteger)index;

That’s a great point. Objective-C metadata doesn’t allow us to model it as a single entity, but a Class + array-of-protocols pair would suffice. I’d rather fold this into a single entrypoint, however, to reduce objc_msgSend overhead. I will note that I haven’t come across a generic Objective-C class that actually has protocol information in it, so it’s not clear whether this would actually get used.

The second is the issue of extensions. Rather than rejecting the use of the generic type parameter, I think we would be better off treating it as though it were a nested typealias. That is, when you write this:

  @interface MySet<__covariant ObjectType: id<NSCopying>> : NSObject

Then MySet.ObjectType is NSCopying. That would allow you to use ObjectType in extensions without particularly caring whether or not you were working with full generics.

I intentionally didn’t propose this because I think it would be very, very confusing. In extensions of Swift-defined generic types, ObjectType would have one meaning: it’s the type for the current specialization and it’s type-safe. In extensions of Objective-C generic types, ObjectType might have either the same meaning as in Swift-defined generic types (i.e., it’s type-safe) or it might be type-erased… and the answer could differ between a static method and an instance method in the same extension! Better to ban references to the generic parameter so it’s obvious to the user that it’s not a notion they can depend on.

The third is that I'm troubled by the invisibility of this feature given its limitations. I understand why you don't want to implement full type erasure, but there should at least be some suggestion of the semantics in the generated headers:

  class MySet<@erased ObjectType: NSCopying>: NSObject

@erased does not have to actually work in user-written code—it's just there in the generated header to mark the type parameter's special semantics.

I started with this, then eventually decided against it, because it’s just noise for the vast majority of users who just use the initializers, methods, and properties that exist on the type. It also implies that this is a general feature of the Swift generics system, which I don’t want: it’s limitations are an artifact of interoperability.

Finally, this is a severable issue, but I think it's worth mentioning: it would be very helpful to expose generic classes back to Objective-C somehow. One of my projects has a CaseInsensitiveDictionary type written in Swift; I have reference-typed NSObject subclasses to expose it to Objective-C, but these can't be generic. Even lightweight generics, even exposed only to Objective-C, would make this code a lot better.

The main issue with this is that Objective-C cannot construct the specialized Swift metatype properly when allocating an instance of this class. We could perhaps export factory initializers to Objective-C that introduce additional “Class” parameters for each of the generic type arguments, so that the Swift-defined factory initializer could form the specialized Swift metatype, then allocate the instance. Or you could fake “alloc” for such types… it’s doable, but messy.

  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Well, I've been drafting this email for several hours. Does that count?

Hah! I’ll take it.

  - Doug

···

On Mar 31, 2016, at 6:11 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote: