Skip to content

Instantly share code, notes, and snippets.

@dabrahams
Last active June 18, 2020 14:58
Show Gist options
  • Select an option

  • Save dabrahams/fef7cf8ff6946477ffecc129867c19e3 to your computer and use it in GitHub Desktop.

Select an option

Save dabrahams/fef7cf8ff6946477ffecc129867c19e3 to your computer and use it in GitHub Desktop.
(https://bugs.swift.org/browse/SR-12692) Demonstrates that the current model for protocol extensions and conditional conformances is… unreasonable.
extension Sequence {
var array: Array<Element> {
print("preallocating", self.underestimatedCount)
return Array(self)
}
}
_ = Array(0..<1000).reversed().array // preallocating 1000
_ = repeatElement(1..<10, count: 200).joined().array // preallocating 0
/// The total number of single-hop index steps made in our test collections.
var totalSteps = 0
/// A collection that tracks the number of single-hop index steps executed.
struct CountSteps0 : RandomAccessCollection {
init(count: Int) { self.count = count }
var count: Int
var startIndex: Int { 0 }
var endIndex: Int { count }
func index(after i: Int) -> Int {
totalSteps += 1
return i + 1
}
func index(before i: Int) -> Int {
totalSteps += 1
return i - 1
}
subscript(i: Int) -> Int { return i }
}
/// A collection that is conditionally Random Access
struct CountSteps1<T: Collection> : Collection {
init(count: Int) { self.count = count }
var count: Int
var startIndex: Int { 0 }
var endIndex: Int { count }
func index(after i: Int) -> Int {
totalSteps += 1
return i + 1
}
subscript(i: Int) -> Int { return i }
}
extension CountSteps1
: RandomAccessCollection, BidirectionalCollection
where T : RandomAccessCollection
{
func index(before i: Int) -> Int {
totalSteps += 1
return i - 1
}
// Even if you don't take advantage of the easy defaults where Index == Int,
// which you typically won't get because your Index will be based on T's index
// type, it's broken. You can see this by uncommenting the two other
// requirements you're supposed to implement if your collection is random
// access.
/*
func index(_ i: Int, offsetBy d: Int) -> Int {
return i + d
}
func distance(from i: Int, to j: Int) -> Int {
return j - i
}
*/
}
extension Collection {
/// Returns the 500th index position if it exists, or `nil` otherwise.
///
/// Complexity: O(1) if `Self` conforms to `RandomAccessCollection`, O(N)
/// otherwise.
func midPoint() -> Index {
index(startIndex, offsetBy: count / 2, limitedBy: endIndex)!
}
}
// CountSteps0 picks up a number of defaults designed to make it easy to create
// `RandomAccessCollection`s.
let x0 = CountSteps0(count: 900)
_ = x0.midPoint()
print("steps:", totalSteps) // 0
/// CountSteps1 doesn't pick up any RandomAccessCollection default
/// implementations, so the complexity guarantee of the `midPoint()` algorithm
/// is broken. This is the case even if we fill in all the requirements you're
/// supposed to have to include in a model of `RandomAccessCollection`
/// (uncomment above).
let x1 = CountSteps1<Range<Int>>(count: 900)
_ = x1.midPoint()
print("steps:", totalSteps) // 450
// Prove that x1 really is a RandomAccessCollection
extension RandomAccessCollection {
var isRandomAccess: Bool { true }
}
extension Collection {
/// Returns `endIndex` offset by `n` positions.
///
/// - Requires: if `Self` does not conform to `BidirectionalCollection`, `n ==
/// 0`.
func offsetEnd(by n: Int) -> Index {
index(endIndex, offsetBy: n)
}
}
print(x1.isRandomAccess)
_ = x1.offsetEnd(by: -1) // TRAP
@dabrahams

Copy link
Copy Markdown
Author

@jens-bc right.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment