[Review] SE-0140: Bridge Optional As Its Payload Or NSNull


(Douglas Gregor) #1

Hello Swift community,

The review of SE-0140 "Bridge Optional As Its Payload Or NSNull" begins now and runs through September 8, 2016. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.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. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.md
Reply text

Other replies
<https://github.com/apple/swift-evolution#what-goes-into-a-review-1>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 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,

-Doug

Review Manager


(Scott James Remnant) #2

:+1::bridge_at_night:

···

Sent from my iPhone

On Sep 2, 2016, at 3:50 PM, Douglas Gregor <dgregor@apple.com> wrote:

Hello Swift community,

The review of SE-0140 "Bridge Optional As Its Payload Or NSNull" begins now and runs through September 8, 2016. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.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. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.md
Reply text

Other replies
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 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,

-Doug

Review Manager

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


(Gwynne Raskind) #3

Hello Swift community,

The review of SE-0140 "Bridge Optional As Its Payload Or NSNull" begins now and runs through September 8, 2016. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.md
  • What is your evaluation of the proposal?

It seems like this would improve the utility of bridging collection types (and plist types in general), and I like that it does something more sensible than before with the disparate bridging behavior of Optional<>. But I do have a couple of questions:

Will specifying a nullable type parameter to collections now become legal in Objective-C? E.g.:
- (void)takeOptionalCollection:(NSArray<id _Nullable> * _Nullable)collection; // Bridge to [Any?]?

If not, is there any way to annotate “I want to be able to use (or not use) NSNull in this collection”? If there is, does doing so affect any behaviors on the Objective-C side? If there isn’t, wouldn’t it make more sense to treat passing an optional to Any as requiring forced unwrapping (including the implied runtime crash behavior), as is the case with other uses of optionals now? I can’t think offhand of any use case where I would want an object, even in pure Swift, where I didn’t know whether it was an optional or not, outside of debugging.

Depending on the answers to these questions, I’m tentatively in favor of this proposal.

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

Honestly, I don’t feel like it is, but my opinion may not be all that valid, since I’ve spent a great deal more time in pure Swift (and interop with pure C) than in interop with Objective-C.

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

Yes, *IF* the semantics of Optional remain consistent, which I’m not yet entirely clear on.

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

I haven’t worked with ObjC bridges in other languages.

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

A quick reading. I was not part of any previous discussion on this topic.

-- Gwynne Raskind
More magic than a mere signature can contain

···

On Sep 2, 2016, at 17:50, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:


(Charles Srstka) #4

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?

Strong -1 as is.

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

Not only do I not believe the problem is significant, but I believe that the proposal *introduces* a new problem which *is* significant, which is the accidental passage of optional arrays to Objective-C. With the existing behavior, such mistakes are immediately obvious as Objective-C receives an opaque object that it cannot use (and probably soon crashes), so they are unlikely to make it past QA testing. For many other cases, particularly when nil is encountered in an array only rarely, this is likely to cause strange and hard-to-debug problems at runtime when NSNull pops up where code wasn’t expecting it (which I would expect to be most Objective-C code), and it might not be detected until after the product ships. In this way, this proposal creates a problem very similar to the problem that Swift was trying to solve with optionals in the first place.

Gwynne brings up the interesting idea of being able to declare Objective-C array parameters as NSArray<id _Nullable>. If this were allowed, and the proposed bridging behavior were *only* invoked for an array so declared, I might soften on this proposal, but as is, I feel that it would be a very large mistake.

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

A major feature of Swift was the concept of optionals, intended to prevent bugs caused by nil pointers showing up where they were not expected. This proposal flies directly in the face of that, and threatens to make it much easier to create bugs caused by NSNull objects showing up where they are not expected. In this way, I feel that it is an almost 180° contradiction of the feel and direction of Swift.

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

The only Objective-C bridges I have used in other languages have been in AppleScript and JXA, neither of which I would expect Swift to use as a model.

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

Read the original discussion, read the proposal, read the comments so far.

Charles

···

On Sep 2, 2016, at 5:50 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:


(Jacob Bandes-Storch) #5

https://github.com/apple/swift-evolution/blob/master/
proposals/0140-bridge-optional-to-nsnull.md

   - What is your evaluation of the proposal?

I tend to agree with other commenters that NSNull does not seem like an

obviously-correct (or -unsurprising) solution to this problem. Rather than
getting "unexpectedly found nil" errors like in Swift, this would start
causing more obtuse errors about missing methods on NSNull.

Users could simply be forced to write `?? NSNull()` if they really want to
use NSNull.

I do think that a warning/error for Optional<T>-to-Any conversion should be
added; something like:

    - (void)test:(id _Nonnull)arg;
    let x: Int? = 3
    test(x) // passing optional type 'Int?' to 'Any' parameter uses an
opaque wrapper; use 'as Any' to silence this warning

Although, I'm not sure that would be a complete solution, because you could
still get around it with generic algorithms. This example is contrived, but:

    extension Array {
        func asAnys() -> [Any] {
            return map { $0 as Any } // no warning here; $0's type is
Element, not known whether optional or non-optional
        }
    }

It might be nice for this warning to occur *only* when passing values to
Obj-C methods. Swift code written using Any would handle them correctly.
However, I'm not sure that's possible, given that the Obj-C importer
recently started using Any instead of AnyObject on purpose to make Obj-C
code more like Swift code in that way.

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

Definitely; the current behavior is surprising and dangerous.

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

Not really; it seems like a Cocoa-ism leaking into the purity/safety of

Swift, more so than most bridging behavior, although perhaps I'm only
saying this because NSNull is much less commonly used than the most other
bridged types.

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

N/A

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

A quick reading of the proposal, plus some experimentation in a sample

project and discussion with others in Slack about an occurrence of the
motivating problem.


(David Hart) #6

I’’ve been migrating a project to Swift 3 and a the piece of code below did not require any modification but had wildly different results:

let myView = viewController.view
superview.addConstraints([
    NSLayoutConstraint(
        item: myView, attribute: .left,
        relatedBy: .equal,
        toItem: superview, attribute: .left,
        multiplier: 1, constant: 0)
])

UIViewController’s view property is typed as UIView! so myView’s type is inferred to UIView? in Swift 3 (previous inferred as UIView! in Swift 2). This will crash in Swift 3 (not in Swift 2), because I think that myView’s optional value is not bridged to the wrapped value in Objective-C when passed to NSLayoutConstraint. Can somebody confirm that this proposal fixes this issue?

Regards,
David.

···

On 03 Sep 2016, at 00:50, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of SE-0140 "Bridge Optional As Its Payload Or NSNull" begins now and runs through September 8, 2016. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.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. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.md
Reply text

Other replies
<https://github.com/apple/swift-evolution#what-goes-into-a-review-1>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 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,

-Doug

Review Manager

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


(Jaden Geller) #7

* What is your evaluation of the proposal?

I support this proposal. I work on a framework, and I was actually able to track down a user-reported crash to the lack of this sort of bridging. An engineer overlooked that an `Optional` value passed into an `Any` parameter, would be forwarded to the Objective-C API as an opaque object. Since we had not tested that particular type, we were unable to detect this issue. Adding this sort of bridging behavior (explicitly in the implementation of our function) is how we were able to fix this issue since the Objective-C API did accept `NSNull`.

Others on the mailing list have brought up concerns that this sort of bridging will defer the detection of API misuse in the case of `NSArray`. I think that, while that is a fair concern, it ignores other cases where this sort of bridging will actually fix crashes that would otherwise occur. Further, I think these concerns forget that fact that `Any` is only used for dynamic APIs. If you'd like a strongly typed `NSArray`, you would use Objective-C lightweight generics. I would actually argue that dynamically bridging `NSNull` to `nil` is most consistent with the current behavior of Swift.

For example, the following cast succeeds since `Int?` is [kinda sorta] a subtype of `Int`.

Optional<Int>.Some(3) as Any as! Int

Thus, in Swift you can treat an `Optional<T>` stored inside an `Any` as if it were simply a `T`. In Objective-C though, this is not the case since there's no equivalent to this sort of dynamic `as!` cast. As such, it would be reasonable to bridge `Optional<T>` to `T | NSNull` such that we can similarly treat `T?` as if it were an `T` in Objective-C. Given that `Any` is used in dynamic APIs that can be passed *any* type, I don't think it is worrisome that this will convert to `NSNull`. If the API is designed to only accept certain types, this should be annotated with generics.

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

I think so. I definitely don't think this issue is as important as fixing numeric bridging, but this seems like a logical improvement to the current bridging behavior.

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

Definitely. In my understanding, Objective-C interop is a major feature of Swift. The Swift team has done a fantastic job improving this since the first release of Swift, and this seems like a logical, incremental step in the same direction.

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

I'm not familiar with other languages with such comprehensive bridging features as Swift. Objective-C is built atop C, and thus has multiple concepts of null (NSNull and NULL), but it has never made sense to bridge between these because Objective-C is a superset of C, not an unrelated language that interops with C (and in this case, these separate types were necessary, something that has been avoided in Swift).

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

I've spent a significant number of hours dealings with issues causes by the lack of bridging, both with `Int8` and friends and here. The lack of bridging of `NSNull` and `nil` is definitely less expected than the lack of numeric bridging, but still problematic for those building Swift wrappers for a dynamic API on top of an Objective-C APIs.

···

On Sep 2, 2016, at 3:50 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of SE-0140 "Bridge Optional As Its Payload Or NSNull" begins now and runs through September 8, 2016. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.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. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.md
Reply text

Other replies
<https://github.com/apple/swift-evolution#what-goes-into-a-review-1>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 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,

-Doug

Review Manager

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


(Fabian Ehrentraud) #8

• What is your evaluation of the proposal?

In favor, as we already ran into crashes after migrating our project to Swift 3.
I'm not completely convinced though that the compiler should ignore nullabiliy markup on id. Also id has a long tradition in the ObjC community to be used instead of NSObject * which does not suffer from the problem.

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

Very much, the compiler should do everything it can to avoid runtime crashes due to programmer errors.

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

Yes, more intelligent and useful bridging makes sense. NSNull can be problematic too, but having an Optional.some value definitely should work on the other side of the bridge too.

• If you have 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?

I even filed a Swift bug (SR-2601) before I found this proposal explaining that nullability on id is ignored since SE-0116.

- Fabian

On 03.09.2016, at 00:50, Douglas Gregor via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>> wrote:

Hello Swift community,

The review of SE-0140 "Bridge Optional As Its Payload Or NSNull" begins now and runs through September 8, 2016. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.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. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.md

Reply text

Other replies
<https://github.com/apple/swift-evolution#what-goes-into-a-review-1>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 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,

-Doug

Review Manager

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


(Pyry Jahkola) #9

I don't feel confident enough about the Swift–Obj-C interop to cast my own vote but I'd like to question this sentiment:

With the existing behavior, such mistakes are immediately obvious as Objective-C receives an opaque object that it cannot use (and probably soon crashes), so they are unlikely to make it past QA testing.

How is this different with NSNull though? If the callee expects an array of NSNumbers for example and uses them (for anything more specific than NSObject), the first NSNull instance will throw an NSInvalidArgumentException basically crashing the program as well:

$ cat example.m
@import Foundation;

int main() {
    id number = NSNull.null;
    NSLog(@"%ld", [number integerValue]);
}

$ clang -fmodules example.m -o example && ./example
2016-09-03 10:47:21.822 example[31488:151700] -[NSNull integerValue]: unrecognized selector sent to instance 0x7fff78561780
(snip...)
libc++abi.dylib: terminating with uncaught exception of type NSException
Abort trap: 6

OTOH, if the only thing the Obj-C code planned to do was immediately convert the NSArray into JSON, then a previously crashing example would now start producing a JSON array with mixed numbers and nulls. But is that kind of code likely in practice?

It would be different though if NSNull just swallowed any messages it receives like `nil` does. There are 3rd party examples <https://github.com/jspahrsummers/libextobjc/blob/master/extobjc/EXTNil.h> of that behaviour, and that would be detrimental to code quality in the case of Swift interop.

— Pyry

···

On 03 Sep 2016, at 03:17, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:


(Joe Groff) #10

Hello Swift community,

The review of SE-0140 "Bridge Optional As Its Payload Or NSNull" begins now and runs through September 8, 2016. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.md
  • What is your evaluation of the proposal?

It seems like this would improve the utility of bridging collection types (and plist types in general), and I like that it does something more sensible than before with the disparate bridging behavior of Optional<>. But I do have a couple of questions:

Will specifying a nullable type parameter to collections now become legal in Objective-C? E.g.:
- (void)takeOptionalCollection:(NSArray<id _Nullable> * _Nullable)collection; // Bridge to [Any?]?

If not, is there any way to annotate “I want to be able to use (or not use) NSNull in this collection”? If there is, does doing so affect any behaviors on the Objective-C side? If there isn’t, wouldn’t it make more sense to treat passing an optional to Any as requiring forced unwrapping (including the implied runtime crash behavior), as is the case with other uses of optionals now? I can’t think offhand of any use case where I would want an object, even in pure Swift, where I didn’t know whether it was an optional or not, outside of debugging.

Depending on the answers to these questions, I’m tentatively in favor of this proposal.

Changing Objective-C is outside of my power. I don't think that giving `_Nullable` a very different meaning is a good idea, though. A feature that feels more in the spirit of Objective-C would be support for class sum types, so you could say that you had an NSArray<NSString* | NSNull*>. (Similarly, you could have a `typedef (NSString*|NSArray*|NSDictionary*|NSNumber*|NSNull*) NSPropertyList;` to obviate the need for `id` for representing property list types.)

-Joe

···

On Sep 2, 2016, at 4:58 PM, Gwynne Raskind via swift-evolution <swift-evolution@swift.org> wrote:

On Sep 2, 2016, at 17:50, Douglas Gregor <dgregor@apple.com> wrote:

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

Honestly, I don’t feel like it is, but my opinion may not be all that valid, since I’ve spent a great deal more time in pure Swift (and interop with pure C) than in interop with Objective-C.

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

Yes, *IF* the semantics of Optional remain consistent, which I’m not yet entirely clear on.

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

I haven’t worked with ObjC bridges in other languages.

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

A quick reading. I was not part of any previous discussion on this topic.

-- Gwynne Raskind
More magic than a mere signature can contain
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #11

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?

Strong -1 as is.

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

Not only do I not believe the problem is significant, but I believe that the proposal *introduces* a new problem which *is* significant, which is the accidental passage of optional arrays to Objective-C. With the existing behavior, such mistakes are immediately obvious as Objective-C receives an opaque object that it cannot use (and probably soon crashes), so they are unlikely to make it past QA testing. For many other cases, particularly when nil is encountered in an array only rarely, this is likely to cause strange and hard-to-debug problems at runtime when NSNull pops up where code wasn’t expecting it (which I would expect to be most Objective-C code), and it might not be detected until after the product ships. In this way, this proposal creates a problem very similar to the problem that Swift was trying to solve with optionals in the first place.

This is a fundamental problem with `Any` in Swift and `id` in Objective-C. There's no way to statically prevent misuse of such APIs. We can, and IMO should, provide warnings when Optionals are used in unconstrained contexts without either being unwrapped or explicitly annotated somehow. That doesn't conflict with this proposal, though.

-Joe

···

On Sep 2, 2016, at 5:17 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

On Sep 2, 2016, at 5:50 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

Gwynne brings up the interesting idea of being able to declare Objective-C array parameters as NSArray<id _Nullable>. If this were allowed, and the proposed bridging behavior were *only* invoked for an array so declared, I might soften on this proposal, but as is, I feel that it would be a very large mistake.

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

A major feature of Swift was the concept of optionals, intended to prevent bugs caused by nil pointers showing up where they were not expected. This proposal flies directly in the face of that, and threatens to make it much easier to create bugs caused by NSNull objects showing up where they are not expected. In this way, I feel that it is an almost 180° contradiction of the feel and direction of Swift.

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

The only Objective-C bridges I have used in other languages have been in AppleScript and JXA, neither of which I would expect Swift to use as a model.

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

Read the original discussion, read the proposal, read the comments so far.

Charles

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


(Joe Groff) #12

The correct behavior would end up falling out with this proposal, yeah, though it would still arguably be more correct for us to force-unwrap IUOs before sticking them in a non-optional Any, instead of putting the wrapped Optional into the Any.

-Joe

···

On Sep 7, 2016, at 12:52 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

I’’ve been migrating a project to Swift 3 and a the piece of code below did not require any modification but had wildly different results:

let myView = viewController.view
superview.addConstraints([
    NSLayoutConstraint(
        item: myView, attribute: .left,
        relatedBy: .equal,
        toItem: superview, attribute: .left,
        multiplier: 1, constant: 0)
])

UIViewController’s view property is typed as UIView! so myView’s type is inferred to UIView? in Swift 3 (previous inferred as UIView! in Swift 2). This will crash in Swift 3 (not in Swift 2), because I think that myView’s optional value is not bridged to the wrapped value in Objective-C when passed to NSLayoutConstraint. Can somebody confirm that this proposal fixes this issue?


(Fabian Ehrentraud) #13

I'd make the warning an error by default

···

Am 09.09.2016 um 16:09 schrieb Fabian Ehrentraud via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>>:

• What is your evaluation of the proposal?

In favor, as we already ran into crashes after migrating our project to Swift 3.
I'm not completely convinced though that the compiler should ignore nullabiliy markup on id. Also id has a long tradition in the ObjC community to be used instead of NSObject * which does not suffer from the problem.

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

Very much, the compiler should do everything it can to avoid runtime crashes due to programmer errors.

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

Yes, more intelligent and useful bridging makes sense. NSNull can be problematic too, but having an Optional.some value definitely should work on the other side of the bridge too.

• If you have 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?

I even filed a Swift bug (SR-2601) before I found this proposal explaining that nullability on id is ignored since SE-0116.

- Fabian

On 03.09.2016, at 00:50, Douglas Gregor via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>> wrote:

Hello Swift community,

The review of SE-0140 "Bridge Optional As Its Payload Or NSNull" begins now and runs through September 8, 2016. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.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. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.md

Reply text

Other replies
<https://github.com/apple/swift-evolution#what-goes-into-a-review-1>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 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,

-Doug

Review Manager

_______________________________________________
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


(Charles Srstka) #14

The difference is that with the existing behavior, any call to *any* object in the array will throw an exception, whether the original was an NSNull or not. This will alert you right away to the fact that you accidentally sent an optional array to an Objective-C API. With this proposal, if the objects in the array are usually non-nil, you might never realize that you accidentally made the array optional, and you won’t find out until some weird edge case results in a nil getting stuck in there and then *surprise!* your users are reporting random crashes in your app that are really hard to track down!

Optional arrays are pretty easy to make by accident. I know that I’ve done it far more than I’ve used anything that required an NSNull. The thing is, if you’ve made one, you usually find out about it pretty quickly because you get compiler errors when passing it to other Swift APIs, and, in the worst case, instant and obvious runtime errors when passing it to Objective-C APIs. With the proposal, accidental optional arrays will suddenly become silent and deadly in Objective-C.

Charles

···

On Sep 3, 2016, at 3:01 AM, Pyry Jahkola <pyry.jahkola@iki.fi> wrote:

On 03 Sep 2016, at 03:17, Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

With the existing behavior, such mistakes are immediately obvious as Objective-C receives an opaque object that it cannot use (and probably soon crashes), so they are unlikely to make it past QA testing.

How is this different with NSNull though? If the callee expects an array of NSNumbers for example and uses them (for anything more specific than NSObject), the first NSNull instance will throw an NSInvalidArgumentException basically crashing the program as well:


(Charlie Monroe) #15

The issue is that when the method doesn't immediately use the values of the array (e.g. stores it for later, or even worse passes them on to another object), the crash appears much later in the program with a message that NSNull doesn't respond to some selector - and that's often hard to debug.

On the other hand, I agree that it's better to use NSNull than how it currently works since this is a valid Swift 3 code:

let arr: [String?] = ["Hello", nil]
NSArray(array: arr)

that will not crash and will produce a valid NSArray. As Douglas Gregor mentioned earlier:

[…] because any Swift type can be placed in an Any, and anything can be bridged to Objective-C. Nearly all of the concerns in this thread are about this aspect of the already-accepted-and-implemented SE-0116: that an optional can get passed through to an Objective-C ‘id’ without being explicitly unwrapped. That behavior exists, and the type of the object seen in Objective-C is an opaque Swift wrapper type.

Playground will show the NSArray as two NSObjects (which are in fact _SwiftValue), which means that the ObjC will crash as well, but with even a more obscure message:

Attempted to dereference an invalid ObjC Object or send it an unrecognized selector.

So, choosing between two evils, I personally vote +1 for adding the NSNull bridging...

···

On Sep 3, 2016, at 10:01 AM, Pyry Jahkola via swift-evolution <swift-evolution@swift.org> wrote:

I don't feel confident enough about the Swift–Obj-C interop to cast my own vote but I'd like to question this sentiment:

On 03 Sep 2016, at 03:17, Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

With the existing behavior, such mistakes are immediately obvious as Objective-C receives an opaque object that it cannot use (and probably soon crashes), so they are unlikely to make it past QA testing.

How is this different with NSNull though? If the callee expects an array of NSNumbers for example and uses them (for anything more specific than NSObject), the first NSNull instance will throw an NSInvalidArgumentException basically crashing the program as well:

$ cat example.m
@import Foundation;

int main() {
    id number = NSNull.null;
    NSLog(@"%ld", [number integerValue]);
}

$ clang -fmodules example.m -o example && ./example
2016-09-03 10:47:21.822 example[31488:151700] -[NSNull integerValue]: unrecognized selector sent to instance 0x7fff78561780
(snip...)
libc++abi.dylib: terminating with uncaught exception of type NSException
Abort trap: 6

OTOH, if the only thing the Obj-C code planned to do was immediately convert the NSArray into JSON, then a previously crashing example would now start producing a JSON array with mixed numbers and nulls. But is that kind of code likely in practice?

It would be different though if NSNull just swallowed any messages it receives like `nil` does. There are 3rd party examples <https://github.com/jspahrsummers/libextobjc/blob/master/extobjc/EXTNil.h> of that behaviour, and that would be detrimental to code quality in the case of Swift interop.

— Pyry

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


(Douglas Gregor) #16

I *think* Charles is saying something slightly different here, and it’s a viewpoint I hadn’t considered before.

We agree that there should be some kind of diagnostic when putting an optional into an Any, because it’s probably not what the user intended. And we know it can happen in ways we cannot diagnose statically, so the diagnostic won’t be perfect. I think Charles is saying that, when this happens, we don’t *want* our Objective-C code to be able to query the value in that optional: in other words, it’s effectively a programmer error to treat such objects as anything more than completely-opaque objects that get passed around any perhaps dealt with properly in Swift code itself.

  - Doug

···

On Sep 6, 2016, at 4:50 PM, Joe Groff <jgroff@apple.com> wrote:

On Sep 2, 2016, at 5:17 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

On Sep 2, 2016, at 5:50 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

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?

Strong -1 as is.

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

Not only do I not believe the problem is significant, but I believe that the proposal *introduces* a new problem which *is* significant, which is the accidental passage of optional arrays to Objective-C. With the existing behavior, such mistakes are immediately obvious as Objective-C receives an opaque object that it cannot use (and probably soon crashes), so they are unlikely to make it past QA testing. For many other cases, particularly when nil is encountered in an array only rarely, this is likely to cause strange and hard-to-debug problems at runtime when NSNull pops up where code wasn’t expecting it (which I would expect to be most Objective-C code), and it might not be detected until after the product ships. In this way, this proposal creates a problem very similar to the problem that Swift was trying to solve with optionals in the first place.

This is a fundamental problem with `Any` in Swift and `id` in Objective-C. There's no way to statically prevent misuse of such APIs. We can, and IMO should, provide warnings when Optionals are used in unconstrained contexts without either being unwrapped or explicitly annotated somehow. That doesn't conflict with this proposal, though.


(Douglas Gregor) #17

I'd make the warning an error by default

Swift 3.0 has already shipped without this warning, so making it an error by default would break source compatibility.

  - Doug

···

On Sep 10, 2016, at 1:20 AM, Fabian Ehrentraud via swift-evolution <swift-evolution@swift.org> wrote:

Am 09.09.2016 um 16:09 schrieb Fabian Ehrentraud via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

• What is your evaluation of the proposal?

In favor, as we already ran into crashes after migrating our project to Swift 3.
I'm not completely convinced though that the compiler should ignore nullabiliy markup on id. Also id has a long tradition in the ObjC community to be used instead of NSObject * which does not suffer from the problem.

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

Very much, the compiler should do everything it can to avoid runtime crashes due to programmer errors.

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

Yes, more intelligent and useful bridging makes sense. NSNull can be problematic too, but having an Optional.some value definitely should work on the other side of the bridge too.

• If you have 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?

I even filed a Swift bug (SR-2601) before I found this proposal explaining that nullability on id is ignored since SE-0116.

- Fabian

On 03.09.2016, at 00:50, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello Swift community,

The review of SE-0140 "Bridge Optional As Its Payload Or NSNull" begins now and runs through September 8, 2016. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.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. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.md
Reply text

Other replies
<https://github.com/apple/swift-evolution#what-goes-into-a-review-1>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 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,

-Doug

Review Manager

_______________________________________________
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


(Joe Groff) #18

I'm not sure I agree with this sentiment. It feels contrary to what you get when you put an Optional in an Any in Swift, since we consider Optional to dynamically be a supertype of its payload:

  let x1: Int? = 1
  let x2: Any = x1
  print(x2 as! Int) // prints 1

-Joe

···

On Sep 6, 2016, at 5:11 PM, Douglas Gregor <dgregor@apple.com> wrote:

On Sep 6, 2016, at 4:50 PM, Joe Groff <jgroff@apple.com> wrote:

On Sep 2, 2016, at 5:17 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

On Sep 2, 2016, at 5:50 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

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?

Strong -1 as is.

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

Not only do I not believe the problem is significant, but I believe that the proposal *introduces* a new problem which *is* significant, which is the accidental passage of optional arrays to Objective-C. With the existing behavior, such mistakes are immediately obvious as Objective-C receives an opaque object that it cannot use (and probably soon crashes), so they are unlikely to make it past QA testing. For many other cases, particularly when nil is encountered in an array only rarely, this is likely to cause strange and hard-to-debug problems at runtime when NSNull pops up where code wasn’t expecting it (which I would expect to be most Objective-C code), and it might not be detected until after the product ships. In this way, this proposal creates a problem very similar to the problem that Swift was trying to solve with optionals in the first place.

This is a fundamental problem with `Any` in Swift and `id` in Objective-C. There's no way to statically prevent misuse of such APIs. We can, and IMO should, provide warnings when Optionals are used in unconstrained contexts without either being unwrapped or explicitly annotated somehow. That doesn't conflict with this proposal, though.

I *think* Charles is saying something slightly different here, and it’s a viewpoint I hadn’t considered before.

We agree that there should be some kind of diagnostic when putting an optional into an Any, because it’s probably not what the user intended. And we know it can happen in ways we cannot diagnose statically, so the diagnostic won’t be perfect. I think Charles is saying that, when this happens, we don’t *want* our Objective-C code to be able to query the value in that optional: in other words, it’s effectively a programmer error to treat such objects as anything more than completely-opaque objects that get passed around any perhaps dealt with properly in Swift code itself.


(Charles Srstka) #19

I’d say my position has three planks on it, and the above is pretty much the first plank: 1) the idea of an array of optionals is a concept that doesn’t really exist in Objective-C, and I do think that passing one to Obj-C ought to be considered a programmer error.

The other two planks would be:

2) Bridging arrays of optionals in this manner could mask the aforementioned programmer error, resulting in unexpected, hard-to-reproduce crashes when an NSNull is accessed as if it were something else, and:

3) Objective-C APIs that accept NSNull objects are fairly rare, so the proposed bridging doesn’t really solve a significant problem (and in the cases where it does, using a map to replace nils with NSNulls is not difficult to write).

Charles

···

On Sep 6, 2016, at 7:11 PM, Douglas Gregor <dgregor@apple.com> wrote:

I *think* Charles is saying something slightly different here, and it’s a viewpoint I hadn’t considered before.

We agree that there should be some kind of diagnostic when putting an optional into an Any, because it’s probably not what the user intended. And we know it can happen in ways we cannot diagnose statically, so the diagnostic won’t be perfect. I think Charles is saying that, when this happens, we don’t *want* our Objective-C code to be able to query the value in that optional: in other words, it’s effectively a programmer error to treat such objects as anything more than completely-opaque objects that get passed around any perhaps dealt with properly in Swift code itself.


(Douglas Gregor) #20

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?

Strong -1 as is.

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

Not only do I not believe the problem is significant, but I believe that the proposal *introduces* a new problem which *is* significant, which is the accidental passage of optional arrays to Objective-C. With the existing behavior, such mistakes are immediately obvious as Objective-C receives an opaque object that it cannot use (and probably soon crashes), so they are unlikely to make it past QA testing. For many other cases, particularly when nil is encountered in an array only rarely, this is likely to cause strange and hard-to-debug problems at runtime when NSNull pops up where code wasn’t expecting it (which I would expect to be most Objective-C code), and it might not be detected until after the product ships. In this way, this proposal creates a problem very similar to the problem that Swift was trying to solve with optionals in the first place.

This is a fundamental problem with `Any` in Swift and `id` in Objective-C. There's no way to statically prevent misuse of such APIs. We can, and IMO should, provide warnings when Optionals are used in unconstrained contexts without either being unwrapped or explicitly annotated somehow. That doesn't conflict with this proposal, though.

I *think* Charles is saying something slightly different here, and it’s a viewpoint I hadn’t considered before.

We agree that there should be some kind of diagnostic when putting an optional into an Any, because it’s probably not what the user intended. And we know it can happen in ways we cannot diagnose statically, so the diagnostic won’t be perfect. I think Charles is saying that, when this happens, we don’t *want* our Objective-C code to be able to query the value in that optional: in other words, it’s effectively a programmer error to treat such objects as anything more than completely-opaque objects that get passed around any perhaps dealt with properly in Swift code itself.

I'm not sure I agree with this sentiment. It feels contrary to what you get when you put an Optional in an Any in Swift, since we consider Optional to dynamically be a supertype of its payload:

   let x1: Int? = 1
   let x2: Any = x1
   print(x2 as! Int) // prints 1

I wasn't expressing an opinion per se; just trying to clear up a perceived misunderstanding.

  - Doug, pretending to be a neutral review manager

···

Sent from my iPhone

On Sep 6, 2016, at 5:21 PM, Joe Groff <jgroff@apple.com> wrote:

On Sep 6, 2016, at 5:11 PM, Douglas Gregor <dgregor@apple.com> wrote:
On Sep 6, 2016, at 4:50 PM, Joe Groff <jgroff@apple.com> wrote:

On Sep 2, 2016, at 5:17 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:
On Sep 2, 2016, at 5:50 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote: