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

Reply via email to