[swift-users] Collection Oddities

Slava Pestov spestov at apple.com
Tue Feb 7 22:57:26 CST 2017


> On Feb 7, 2017, at 8:14 PM, Guillaume Lessard via swift-users <swift-users at swift.org> wrote:
> 
> I keep running into weird things with Swift 3 Collections.
> 
> A) Collection has its count property defined as an Int via IndexDistance:
> 
> public protocol Collection : Indexable, Sequence {
>    associatedtype IndexDistance : SignedInteger = Int  // line 182 in Collection.swift
>    public var count: IndexDistance { get }             // line 776
> }

This declaration specifies that the *default* associated type is Int, not that it’s *always* Int. A Collection implementation is free to use a different type as its IndexDistance if it wants.

> Given this, why can’t `count` be treated as an Int?
> 
> func intCount<C: Collection>(of c: C) -> Int {
>  return c.count // nope!
> }
> 
> A numericCast is required here in order to “convert” what is known-to-be-an-Int to an actual Int.
> Why this is so? IndexDistance is defined such that it must be an integer (“A type that represents the number of steps between a pair of indices”)...
> What is gained by making it so generic that it can’t simply be an Int?
> 
> 
> B) Collection.Indices is a Collection of Index, *but*
> - Collection.Indices.Index is not the same type as Collection.IndexDistance

There’s no reason for them to be the same type for a generic Collection, just because they both happen to be Int for Array, though.

> - Collection.Indices.Iterator.Element is somehow not the automatically the same type as Collection.Index
> (ugh)

I think you’re right that this is a generics limitation - we’re waiting on ‘where clauses for associated types’ to make these two types equivalent.
 
> 
> The second seems like a conditional conformance issue, but the first one is baffling.
> 
> I did find that with String.CharacterView:
> String.CharacterView.Indices.Index != String.CharacterView.IndexDistance
> although, as expected,
> String.CharacterView.Indices.IndexDistance == String.CharacterView.IndexDistance
> (which provides an clear workaround.)
> 
> 
> ***
> I wanted to extend Collection with concurrentPerform; which seems simple on the surface:
> 
> extension Collection {
>  public func concurrentPerform(task: @escaping (Self.Iterator.Element) -> Void) {
>    DispatchQueue.concurrentPerform(iterations: count) {
>      iteration in
>      task(self[indices[iteration]])
>    }
>  }
> }
> … but that won’t do.
> 
> The closest thing I found that does work is this:
> 
> extension Collection {
>  public func concurrentPerform(task: @escaping (Self.Iterator.Element) -> Void) {
>    let count: Int = numericCast(self.count)
>    let indexList = (0..<count).map { index(startIndex, offsetBy: numericCast($0)) }
> 
>    DispatchQueue.concurrentPerform(iterations: count) {
>      iteration in
>      task(self[indexList[iteration]])
>    }
>  }
> }
> 
> Note the unfortunate creation of an array of Index.
> Generic Collection code is exhausting!

Maybe in this case it makes more sense to define this on RandomAccessCollection or even just Array instead?

Slava

> 
> Cheers,
> Guillaume Lessard
> 
> _______________________________________________
> swift-users mailing list
> swift-users at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users



More information about the swift-users mailing list