Swift should have first-class immutable collections

Originator:robnapier
Number:rdar://17181544 Date Originated:05-Jun-2014
Status:Open Resolved:
Product:Swift Product Version:
Classification: Reproducible:
 
The Array mutability and copying rules in Swift are confusing, error-prone, and make it difficult to correctly implement concurrency. The basic Array should be immutable, just like in ObjC.

We need an immutable array as a core language feature (not a bolt-on via bridging to NSArray bridging) that matches closely the patterns we've developed for NSArray. We should encourage immutability as much as possible. This seems a very hard thing to fix later, once mutable arrays are the norm.

In any case, the current "sometimes it's shared but then it can suddenly become unshared if you extend it" is far too confusing. Either it needs to be shared or not shared; mutable or immutable; it shouldn't suddenly change. The fact that it has different behaviors than Dictionary makes it incredibly confusing.

As a possible implemention, I suggest a base "ForwardIterable" protocol (based on the current ForwardIndex) that everything builds up from, such as:

// Defines Swift methods that at a minimum match NSFastEnumeration
protocol ForwardIterable {
  typealias Element
  func succ() -> Element
}

// A collection of objects
protocol Collection : ForwardIterable {
}

// An ordered collection of objects
protocol Sequence : Collection {
  subscript (i: Int) -> Element { get }
}

// An immutable array
struct Array<T> : Sequence {
  typealias Element = T
  func succ() -> T { ... }
  subscript (i: Int) -> T { get { ... } }
}

// A mutable, ordered collection of objects
protocol MutableSequence : Sequence {
  subscript (i: Int) -> Element { get set }
}

// A mutable array; can be passed anywhere that accepts a Sequence
struct MutableArray<T> : MutableSequence {
  typealias Element = T
  func succ() -> T { ... }
  subscript (i: Int) -> T { get { ... } set { ... } }
}


I recommend that [1,2,3] return an immutable array (like in ObjC). Another syntax such as [[1,2,3]] should be used for literal mutable arrays. The easiest, most common form should be immutable.

Scala offers a rich collection hierarchy (starting at Traversable and working up). There seems to be a lot of Scala influence in the types already, so it would be nice to more closely follow the area already explored there. But it is also very reasonable to use more ObjC terms like "Enumerator" rather than "Iterator." Swift does not need to look more like Scala, but their collection classes are a good place to look for inspiration.

Comments

Agree. This has been widely noticed.

Agree. Further discussion in other venues: A gist, showing the usual risks of mutability and the inconsistency with dictionaries: https://gist.github.com/algal/1ca25254e16504368c48

A developer forum thread: https://devforums.apple.com/thread/228695?tstart=0 .

A Stack Overflow question: http://stackoverflow.com/questions/24090741/how-do-you-create-an-immutable-array-in-swift

By alexisgallagher at June 6, 2014, 10:11 p.m. (reply...)

Swift collections appear to be immutable in that they are copy on write or copy on assign (with the compiler possibly optimizing safe paths).

You can see this if you create an array property with let. Performing append anywhere other than init generates a compiler warning.

By brian.nickel at June 6, 2014, 4:46 a.m. (reply...)

A distinct syntax is needed. We should be allowed to create an array of arrays.

By lokisnake at June 6, 2014, 1:22 a.m. (reply...)

Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!