First of all, I'm really happy with this proposal so far. I really appreciate the work that's been done to improve Swift and Objective-C interoperability.
Now, question: Does this proposal also improve bridging from Objective-C to Swift or only the other direction? For example, let's say an `[Any]` contains either `Foo` or `NSNull`. Could this bridge to Swift as `[Foo?]`? I'd like to be able to write let x = [Foo(), Foo(), nil] as [Foo?] as [Any] let x2 = x as! [Foo?] // Already works let y = [Foo(), Foo(), NSNull()] as [Any] let y2 = y as! [Foo?] // Should work and have this succeed. That is, an `[Any]` can be cast to `[T?]` either if it only contains `T` and `NSNull` or if it only contains `T?`, not some combination of both. > On Aug 24, 2016, at 11:20 AM, Douglas Gregor via swift-evolution > <swift-evolution@swift.org> wrote: > > >> On Aug 24, 2016, at 4:16 AM, David Rönnqvist <david.ronnqv...@gmail.com >> <mailto:david.ronnqv...@gmail.com>> wrote: >> >> I have some problems understanding the scope of this proposal. More >> specifically if it’s limited to arrays and dictionaries or if it’s broader >> than that, and if it’s limited to objects that originate in Swift or if the >> same applies for objects that originate in Objective-C code. > > It’s broader than that. It affects any Optional value that is put into an > ‘Any’ and passed to Objective-C. Note, however, that if you have a nullable > parameter in Objective-C, e.g., > > -(void)methodWithObject:(nullable id)object; > > Which comes into Swift as > > func method(with object: Any?) > > Then ‘nil’ will be passed through as ‘nil’. This only affects the case where > you’re passing a Swift optional to a non-optional parameter: > > -(void)methodWithNonNullObject:(nonnull id)object; > > func method(withNonNullObject object: Any) > >> >> For me, it makes sense that Swift arrays of type [C?] and [Any] would bridge >> to Objective-C as NSArrays bridge nils to NSNull. That feels like the most >> natural way of representing those missing values in Objective-C. > > Right. The alternative is that nil values bridge to an opaque box type known > only to the Swift runtime. NSNull seems strictly better here, because > Objective-C code can reason about it. > >> For dictionaries of type [K:C?] and [K:Any] I feel that bridging Swift nils >> to NSNull is pretty straight forward and allows for the distinction of a key >> with no value and a key with an explicit nil value. However, I feel that the >> same doesn’t work in the other direction. If a NSNull value in an >> Objective-C NSDictionary would bridge to a nil value it wouldn’t be possible >> to distinguish between a key without a value and key with a nil value >> (something one might have to do when checking the KVO change dictionary). > > NSNulls are handled dynamically. If you wanted to check whether Objective-C > put an ‘NSNull’ in there explicitly, you can do so with “as? NSNull”. If > instead you do “as? SomeType?”, the NSNull will become the ‘nil’ value in the > SomeType. > >> >> There are also some APIs that make a distinction between NSNull and nil, for >> example action(for:forKey:) on CALayerDelegate. Does this proposal have any >> impact on those APIs? > > That method returns “CAAction?”, so ‘nil’ will come through as ‘nil’ and > NSNull can be stored in the .some(x). > > - Doug > >> >> - David >> >>> On 24 Aug 2016, at 00:36, Douglas Gregor via swift-evolution >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>> >>> Introduction >>> >>> 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/> >>> >>> <https://github.com/jckarter/swift-evolution/blob/be49e08f56450ffea394306198bcd25f58915e30/proposals/XXXX-bridge-optional-to-nsnull.md#motivation>Motivation >>> >>> 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 >>> >>> _______________________________________________ >>> swift-evolution mailing list >>> swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>> https://lists.swift.org/mailman/listinfo/swift-evolution >>> <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