I'd imagine an implementation something like this (but, in the bridging logic,
not as a top level function…):
func dynamicCast<T>(_ array: [Any]) -> [T?] {
var _type: CollectionCastType?
func assertType(_ checkType: CollectionCastType) {
guard let type = _type else {
_type = checkType
return
}
assert(type == checkType)
}
return array.map { element in
switch element {
case let element as T?:
assertType(.normal)
return element
case let element as T:
assertType(.lifting)
return element
case is NSNull:
assertType(.lifting)
return nil
default:
fatalError("Incorrect types")
}
}
}
Essentially, it either identifies an array as being entirely of `T?` (normal
cast type) or being entirely of both `T` and `NSNull` (lifted cast type) and
returns the proper value. In the bridging logic, this would be done lazily
(like how `NSArray`s bridged to `[T]` lazily check element type on access).
What are your thoughts?
> On Aug 24, 2016, at 4:08 PM, Jaden Geller <[email protected]> wrote:
>
> 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
>> <[email protected] <mailto:[email protected]>> wrote:
>>
>>
>>> On Aug 24, 2016, at 4:16 AM, David Rönnqvist <[email protected]
>>> <mailto:[email protected]>> 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
>>>> <[email protected] <mailto:[email protected]>> 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
>>>> [email protected] <mailto:[email protected]>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>
>>
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected] <mailto:[email protected]>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution