Created
July 9, 2016 01:31
-
-
Save beccadax/b36ef130873b752d4c6f7ee3c157d07d to your computer and use it in GitHub Desktop.
Non-stdlib initial prototype of IncompleteRange design
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: Playground - noun: a place where people can play | |
func withSampleData(f: (inout [Int]) -> Void) { | |
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9] | |
f(&array) | |
} | |
// IncompleteRange, nil lowerBound | |
withSampleData { array in | |
array[..<6] | |
array.prefix(upTo: 6) | |
// On other (unproposed) APIs: | |
array.replaceSubrange(..<1, with: [0]) | |
array.removeSubrange(..<1) | |
} | |
// IncompleteClosedRange, nil lowerBound | |
withSampleData { array in | |
array[...6] | |
array.prefix(through: 6) | |
// On other (unproposed) APIs: | |
array.replaceSubrange(...1, with: [1]) | |
array.removeSubrange(...1) | |
} | |
// IncompleteRange, nil upperBound | |
withSampleData { array in | |
array[6..<] | |
array.suffix(from: 6) | |
// On other (unproposed) APIs: | |
array.replaceSubrange(6..<, with: [10]) | |
array.removeSubrange(6..<) | |
} | |
// IncompleteClosedRange, nil upperBound | |
// These are all commented out because they crash, and they crash because they cover `endIndex`. Should postfix `...` be left out as seemingly useless, or kept for symmetry? | |
//withSampleData { array in | |
// array[6...] | |
// // No equivalent | |
// // On other (unproposed) APIs: | |
// array.replaceSubrange(6..., with: [11]) | |
// array.removeSubrange(6...) | |
//} | |
// Test that the possible infix form doesn't introduce new conflicts | |
withSampleData { array in | |
array[1 ..< 6] | |
array[nil ..< 6] | |
array[1 ..< nil] | |
array[nil ..< nil] | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension Collection { | |
internal func _fullRange() -> Range<Index> { | |
return startIndex ..< endIndex | |
} | |
internal func _fullRange() -> ClosedRange<Index> { | |
return startIndex ... endIndex | |
} | |
/// Accesses a contiguous subrange of the collection's elements, with the | |
/// missing bounds filled in with `startIndex` and `endIndex`. | |
/// | |
/// The accessed slice uses the same indices for the same elements as the | |
/// original collection. Always use the slice's `startIndex` property | |
/// instead of assuming that its indices start at a particular value. | |
/// | |
/// This example demonstrates getting a slice of an array of strings, finding | |
/// the index of one of the strings in the slice, and then using that index | |
/// in the original array. | |
/// | |
/// let streets = ["Adams", "Bryant", "Channing", "Douglas", "Evarts"] | |
/// let streetsSlice = streets[2..<] | |
/// print(streetsSlice) | |
/// // Prints "["Channing", "Douglas", "Evarts"]" | |
/// | |
/// let index = streetsSlice.index(of: "Evarts") // 4 | |
/// print(streets[index!]) | |
/// // Prints "Evarts" | |
/// | |
/// - Parameter bounds: A range of the collection's indices. The bounds of | |
/// the range must be valid indices of the collection. A `nil` `lowerBound` | |
/// will be interpreted as `startIndex`; a `nil` `upperBound` will be | |
/// interpreted as `endIndex`. | |
public subscript(range: IncompleteClosedRange<Index>) -> SubSequence { | |
return self[range.completed(with: _fullRange())] | |
} | |
/// Accesses a contiguous subrange of the collection's elements, with the | |
/// missing bounds filled in with `startIndex` and `endIndex`. | |
/// | |
/// The accessed slice uses the same indices for the same elements as the | |
/// original collection uses. Always use the slice's `startIndex` property | |
/// instead of assuming that its indices start at a particular value. | |
/// | |
/// This example demonstrates getting a slice of an array of strings, finding | |
/// the index of one of the strings in the slice, and then using that index | |
/// in the original array. | |
/// | |
/// let streets = ["Adams", "Bryant", "Channing", "Douglas", "Evarts"] | |
/// let streetsSlice = streets[2..<] | |
/// print(streetsSlice) | |
/// // Prints "["Channing", "Douglas", "Evarts"]" | |
/// | |
/// let index = streetsSlice.index(of: "Evarts") // 4 | |
/// print(streets[index!]) | |
/// // Prints "Evarts" | |
/// | |
/// - Parameter bounds: A range of the collection's indices. The bounds of | |
/// the range must be valid indices of the collection. A `nil` `lowerBound` | |
/// will be interpreted as `startIndex`; a `nil` `upperBound` will be | |
/// interpreted as `endIndex`. | |
public subscript(range: IncompleteRange<Index>) -> SubSequence { | |
return self[range.completed(with: _fullRange())] | |
} | |
} | |
// These are future expansions: other APIs which could benefit from incomplete range support. | |
extension RangeReplaceableCollection { | |
public mutating func removeSubrange(_ bounds: IncompleteRange<Self.Index>) { | |
removeSubrange(bounds.completed(with: _fullRange())) | |
} | |
public mutating func replaceSubrange<C where C : Collection, C.Iterator.Element == _Element>(_ subrange: IncompleteRange<Self.Index>, with newElements: C) { | |
replaceSubrange(subrange.completed(with: _fullRange()), with: newElements) | |
} | |
public mutating func removeSubrange(_ bounds: IncompleteClosedRange<Self.Index>) { | |
removeSubrange(bounds.completed(with: _fullRange())) | |
} | |
public mutating func replaceSubrange<C where C : Collection, C.Iterator.Element == Iterator.Element>(_ subrange: IncompleteClosedRange<Self.Index>, with newElements: C) { | |
replaceSubrange(subrange.completed(with: _fullRange()), with: newElements) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
prefix operator ... {} | |
postfix operator ... {} | |
/// A closed interval which may have one or both of its bounds unknown. A | |
/// completed `ClosedRange` or `CountableClosedRange` can be created from an | |
/// `IncompleteClosedRange` by calling the `completed(with:)` method. | |
public struct IncompleteClosedRange<Bound: Comparable> { | |
public let lowerBound: Bound? | |
public let upperBound: Bound? | |
/// Returns a `ClosedRange` with the bounds of `self`, with any missing bounds | |
/// filled in from `defaultBounds`. | |
public func completed(with defaultBounds: ClosedRange<Bound>) -> ClosedRange<Bound> { | |
return (lowerBound ?? defaultBounds.lowerBound) ... (upperBound ?? defaultBounds.upperBound) | |
} | |
} | |
extension IncompleteClosedRange where Bound: Strideable, Bound.Stride: SignedInteger { | |
/// Returns a `CountableClosedRange` with the bounds of `self`, with any | |
/// missing bounds filled in from `defaultBounds`. | |
public func completed(with defaultBounds: CountableClosedRange<Bound>) -> CountableClosedRange<Bound> { | |
return (lowerBound ?? defaultBounds.lowerBound) ... (upperBound ?? defaultBounds.upperBound) | |
} | |
} | |
/// Constructs an `IncompleteClosedRange` with the indicated `lowerBound`. The | |
/// `upperBound` is left empty and will be filled in when the range is completed. | |
public postfix func ... <Bound: Comparable>(lowerBound: Bound) -> IncompleteClosedRange<Bound> { | |
return IncompleteClosedRange(lowerBound: lowerBound, upperBound: nil) | |
} | |
/// Constructs an `IncompleteClosedRange` with the indicated `upperBound`. The | |
/// `lowerBound` is left empty and will be filled in when the range is completed. | |
public prefix func ... <Bound: Comparable>(upperBound: Bound) -> IncompleteClosedRange<Bound> { | |
return IncompleteClosedRange(lowerBound: nil, upperBound: upperBound) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
prefix operator ..< {} | |
postfix operator ..< {} | |
/// A half-open interval which may have one or both of its bounds unknown. A | |
/// completed `Range` or `CountableRange` can be created from an | |
/// `IncompleteRange` by calling the `completed(with:)` method. | |
public struct IncompleteRange<Bound: Comparable> { | |
public let lowerBound: Bound? | |
public let upperBound: Bound? | |
/// Returns a `Range` with the bounds of `self`, with any missing bounds filled | |
/// in from `defaultBounds`. | |
public func completed(with defaultBounds: Range<Bound>) -> Range<Bound> { | |
return (lowerBound ?? defaultBounds.lowerBound) ..< (upperBound ?? defaultBounds.upperBound) | |
} | |
} | |
extension IncompleteRange where Bound: Strideable, Bound.Stride: SignedInteger { | |
/// Returns a `CountableRange` with the bounds of `self`, with any missing | |
/// bounds filled in from `defaultBounds`. | |
public func completed(with defaultBounds: CountableRange<Bound>) -> CountableRange<Bound> { | |
return (lowerBound ?? defaultBounds.lowerBound) ..< (upperBound ?? defaultBounds.upperBound) | |
} | |
} | |
/// Constructs an `IncompleteRange` with the indicated `lowerBound`. The | |
/// `upperBound` is left empty and will be filled in when the range is completed. | |
public postfix func ..< <Bound: Comparable>(lowerBound: Bound) -> IncompleteRange<Bound> { | |
return IncompleteRange(lowerBound: lowerBound, upperBound: nil) | |
} | |
/// Constructs an `IncompleteRange` with the indicated `upperBound`. The | |
/// `lowerBound` is left empty and will be filled in when the range is completed. | |
public prefix func ..< <Bound: Comparable>(upperBound: Bound) -> IncompleteRange<Bound> { | |
return IncompleteRange(lowerBound: nil, upperBound: upperBound) | |
} | |
// XXX I notice that, although IncompleteRange permits the construction of ranges | |
// with both or neither bound specified, this design doesn't include operators that | |
// would allow that. If there were infix ..< and ... operators, we would be able | |
// to. | |
public func ..< <Bound: Comparable>(lowerBound: Bound?, upperBound: Bound?) -> IncompleteRange<Bound> { | |
return IncompleteRange(lowerBound: lowerBound, upperBound: upperBound) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment