Support repeating initializers with closures not just values

For RangeReplaceableCollection.append(repeating: count:), you can replace that with

append(contentsOf: repeatElement(repeating, count: count))

For RangeReplaceableCollection.append(count: generator:), it would seem better to make something like

func generateElements<T>(count n: Int, generator: @escaping () ??? -> T) -> Generated<T>

and carry that as a Sequence to use with the existing initializers, append, insert, and replace methods that take sequences. The problem that the throws/rethrows system can't work because we cascade the throw-ability differently. We would have to make different overloads; or keep the way you use the rethrow system here. I think we need to work on the side-effect design some more, like I read a Microsoft paper once (from a link from a post on this forum) with a programming language that generalized exception propagation for any side effect.

The extension for Set can be implemented with existing members once a generator Sequence is done. (The update(count: capacityIncrease: generator) method can be reworked with union(_:) or formUnion(_:). The initializer can be replaced with the existing sequence-taking one.)

The three members of the second extension to RangeReplaceableCollection can be reworked with existing members once a generator Sequence is done. The prepend method should be a general extension method with Sequence and single-Element versions, like insert and append. There is a second concern: the very use of Index in the closures is fundamentally broken.

  • The operations to add/remove/replace elements can invalidate every element's index, so use of at's value after the first call to insert is a fatal error right there.
  • The index of a new element is determined solely by the collection. You cannot guess a new element's index from an existing element's index. (And from the previous point, said existing element's index value was decertified when the insert occurred!)

Even if they worked in testing, their core code violates the rules of RangeReplaceableCollection.

The extension for Dictionary isn't illegal since it manipulates Keys instead of Indexes. But its two members can be reworked with a map.

public extension Dictionary where Key: Strideable {
   @discardableResult @inlinable
    mutating func insert(count: Int, fromKey: Key, generator: (_ key: Key) throws -> Value) rethrows -> Dictionary<Key, Value> {
        precondition(count >= 0, "Negative count: \(count).")
        reserveCapacity(self.count + count)
        try merge((0..<count).map { fromKey.advanced(by: $0 as! Key.Stride) }.map { ($0, try generator($0)) }) { $1 }
        return self
    }

    @inlinable
    init(count: Int, fromKey: Key, generator: (_ key: Key) throws -> Value) rethrows {
        try self.init(uniqueKeysWithValues: (0..<count).map { fromKey.advanced(by: $0 as! Key.Stride) }.map { ($0, try generator($0)) })
    }
}
1 Like