:orphan: .. warning:: This proposal was rejected. We ultimately decided to keep Array as a dual-representation struct. Bridging Container Protocols to Class Clusters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ I think that attempting to bridge ``NSArray`` to a concrete type like ``Array`` will be a poor compromise, losing both the flexibility of NSArray's polymorphism and the performance afforded by ``Array``'s simplicity. Here's what I propose instead: - Rename our current ``Array`` back to ``Vector`` or perhaps something like ``ContiguousArray``. - Redefine ``Array`` as a refinement of the ``Collection`` protocol that has integer indices. - Implement an ``ArrayOf`` generic container, like ``AnyIterator`` and ``AnySequence``, that can hold an arbitrary type conforming to ``Array``. - Bridge ``NSArray`` from ObjC to Swift ``ArrayOf`` with value semantics. - Bridge ``Array``-conforming types with class element types in Swift to ObjC as ``NSArray``. Although I'll be talking about arrays in this proposal, I think the same approach would work for ``NSDictionary`` and ``NSSet`` as well, mapping them to generic containers for associative map and unordered container protocols respectively. NSArray vs Array ================ Despite their similar names, ``NSArray`` and Swift's ``Array`` have fundamentally incompatible design goals. As the root of a class cluster, ``NSArray`` provides abstraction over many underlying data structures, trading weaker algorithmic guarantees for better representational flexibility and implementation encapsulation. Swift's ``Array``, on the other hand, is intended to be a direct representation of a contiguous region of memory, more like a C array or C++'s ``vector``, minimizing abstraction in order to provide tight algorithmic guarantees. Many ``NSArray`` implementations are lazy, such as those over KVO properties or Core Data aggregates, and transforming them to concrete ``Array``\ s would have unintended semantic effects. And on the other side, the overhead of having to accommodate an arbitrary ``NSArray`` implementation inside ``Array`` destroys ``Array`` as a simple, high-performance container. Attempting to bridge these two types will result in an unattractive compromise to both sides, weakening the algorithmic guarantees of Array while forgoing the full flexibility of ``NSArray``. "Array" as a Refinement of the Collection Protocol ================================================== Swift's answer to container polymorphism is its generics system. The ``Collection`` protocol provides a common interface to indexable containers that can be used generically, which is exactly what ``NSArray`` provides in Cocoa for integer-indexable container implementations. ``Array`` could be described as a refinement of ``Collection`` with integer indices:: protocol Array : Collection { where IndexType == Int } protocol MutableArray : MutableCollection { where IndexType == Int } The familiar ``NSArray`` API can then be exposed using default implementations in the ``Array`` protocol, or perhaps even on the more abstract ``Collection`` and ``Sequence`` protocols, and we can bridge ``NSArray`` in a way that plays nicely with generic containers. This naming scheme would of course require us to rename the concrete ``Array`` container yet again. ``Vector`` is an obvious candidate, albeit one with a C++-ish bent. Something more descriptive like ``ContiguousArray`` might feel more Cocoa-ish. The ArrayOf Type =================== Although the language as implemented does not yet support protocol types for protocols with associated types, DaveA devised a technique for implementing types that provide the same effect in the library, such as his ``AnyIterator`` and ``AnySequence`` containers for arbitrary ``Stream`` and ``Sequence`` types. This technique can be extended to the ``Array`` protocol, using class inheritance to hide the concrete implementing type behind an abstract base:: // Abstract base class that forwards the Array protocol class ArrayOfImplBase { var startIndex: Int { fatal() } var endIndex: Int { fatal() } func __getitem__(_ i: Int) -> T { fatal() } // For COW func _clone() -> Self { fatal() } } // Concrete derived class containing a specific Array implementation class ArrayOfImpl : ArrayOfImplBase { var value: ArrayT var startIndex: Int { return value.startIndex } var endIndex: Int { return value.endIndex } func __getitem__(_ i: Int) -> T { return __getitem__(i) } // For COW func _clone() -> Self { return self(value) } } // Wrapper type that uses the base class to erase the concrete type of // an Array struct ArrayOf : Array { var value: ArrayOfImplBase var startIndex: Int { return value.startIndex } var endIndex: Int { return value.endIndex } func __getitem__(_ i: Int) -> T { return value.__getitem__(i) } init(arr: ArrayT) { value = ArrayOfImpl(arr) } } The mutable variant can use COW optimization to preserve value semantics:: struct MutableArrayOf : MutableArray { /* ...other forwarding methods... */ func __setitem__(_ i: Int, x: T) { if !isUniquelyReferenced(value) { value = value._clone() } value.__setitem__(i, x) } } Bridging ``NSArray`` into Swift =============================== We could simply make ``NSArray`` conform to ``Array``, which would be sufficient to allow it to be stored in an ``ArrayOf`` container. However, a good experience for ``NSArray`` still requires special-case behavior. In particular, ``NSArray`` in Cocoa is considered a value class, and best practice dictates that it be defensively ``copy``-ed when used. In Swift, we should give bridged NSArrays COW value semantics by default, like ``NSString``. One way to handle this is by adding a case to the ``ArrayOf`` implementation, allowing it to either contain a generic value or an ``NSArray`` with COW semantics. Bridging Swift Containers to ``NSArray`` ======================================== We could have an implicit conversion to ``NSArray`` from an arbitrary type conforming to ``Array`` with a class element type, allowing ObjC APIs to work naturally with generic Swift containers. Assuming we had support for ``conversion_to`` functions, it could look like this:: class NSArrayOf : NSArray { /* ...implement NSArray methods... */ } extension NSArray { @conversion_to func __conversion_to< ArrayT: Array where ArrayT.Element : class >(arr: ArrayT) -> NSArray { return NSArrayOf(arr) } } ``NSArray`` has reference semantics in ObjC, which is a mismatch with Swift's value semantics, but because ``NSArray`` is a value class, this is probably not a problem in practice, because it will be ``copy``-ed as necessary as a best practice. There also needs to be a special case for bridging an ``ArrayOf`` that contains an ``NSArray``; such a container should be bridged directly back to the underlying unchanged ``NSArray``.