[swift-evolution] [Proposal draft] Bridge Optional As Its Payload Or NSNull

Douglas Gregor dgregor at apple.com
Tue Aug 23 17:36:33 CDT 2016


Optionals can be used as values of the type Any, but only bridge as opaque objects in Objective-C. We should bridge Optionals with some value by bridging the wrapped value, and bridge nils to the NSNull singleton.

Swift-evolution thread: TBD <https://lists.swift.org/pipermail/swift-evolution/>

SE-0116 <https://github.com/apple/swift-evolution/blob/master/proposals/0116-id-as-any.md> changed how Objective-C's id and untyped collections import into Swift to use the Any type. This makes it much more natural to pass in Swift value types such as String and Array, but introduces the opportunity for optionality mismatches, since an Any can contain a wrapped Optional value just like anything else. Our current behavior, where Optional is given only the default opaque bridging behavior, leads to brittle transitivity problems with collection subtyping. For example, an array of Optional objects bridges to an NSArray of opaque objects, unusable from ObjC:

class C {}
let objects: [C?] = [C(), nil, C()]
The more idiomatic mapping would be to use NSNull or some other sentinel to represent the missing values (since NSArray cannot directly store nil). Counterintuitively, this is in fact what you get if you bridge an array of Any with nil elements:

class C {}
let objects: [Any] = [C(), nil as C?, C()]
though with an opaque box taking the place of the standard NSNull sentinel. Since there's a subtype relationship between T and Optional<T>, it's also intuitive to expect that the bridging operation be consistent between T and occupied values of Optional<T>.

 <https://github.com/jckarter/swift-evolution/blob/be49e08f56450ffea394306198bcd25f58915e30/proposals/XXXX-bridge-optional-to-nsnull.md#proposed-solution>Proposed solution

When an Optional<T> value is bridged to an Objective-C object, if it contains some value, that value should be bridged; otherwise, NSNull or another sentinel object should be used.

 <https://github.com/jckarter/swift-evolution/blob/be49e08f56450ffea394306198bcd25f58915e30/proposals/XXXX-bridge-optional-to-nsnull.md#detailed-design>Detailed design

some maps to the bridged value
none maps to NSNull
if we don't want to lose information about nested optionals, we'd need a unique SwiftNull object for every optional type, so that .some(.none) maps to NSNull and .none maps to SwiftNull(T?)
 <https://github.com/jckarter/swift-evolution/blob/be49e08f56450ffea394306198bcd25f58915e30/proposals/XXXX-bridge-optional-to-nsnull.md#impact-on-existing-code>Impact on existing code

This change has no static source impact, but changes the dynamic behavior of the Objective-C bridge. From Objective-C's perspective, Optionals that used to bridge as opaque objects will now come in as semantically meaningful Objective-C objects. This should be a safe change, since existing code should not be relying on the behavior of opaque bridged objects. From Swift's perspective, values should still be able to round-trip from Optional to Any to id to Anyand back by dynamic casting.

 <https://github.com/jckarter/swift-evolution/blob/be49e08f56450ffea394306198bcd25f58915e30/proposals/XXXX-bridge-optional-to-nsnull.md#alternatives-considered>Alternatives considered

Do nothing
Attempt to trap or error when Optionals are used as Anys -- would be good QoI to warn, but it can't be prevented, and is occasionally desired

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160823/f9692666/attachment.html>

More information about the swift-evolution mailing list