Skip to content

Instantly share code, notes, and snippets.

@xwu
Forked from erica/rangeoperators.md
Last active April 9, 2016 09:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xwu/bf82f1acffaa5f2cb9ae032bfe2dd9b4 to your computer and use it in GitHub Desktop.
Save xwu/bf82f1acffaa5f2cb9ae032bfe2dd9b4 to your computer and use it in GitHub Desktop.

Completing the Swift 3 range operator suite

Introduction

We propose to complete Swift's suite of range operators to introduce a fully open range (<.<) and the second complementary half-open range (<..).

This proposal was discussed on the Swift Evolution list in the Feature proposal: Range operator with step thread. (Direct link to original thread)

Motivation

At this time, you can build switch pattern matching cases for "low <= x <= high" and "low <= x < high" but you cannot create corresponding cases for "low < x <= high" or "low < x < high". Updating Range for Swift 3 offers a window of opportunity to complete the range operators for use in pattern matching, numerical applications, and teaching scenarios. By comparison, Perl now supports four range operators: .. (equivalent to Swift's ...), ..^ (equivalent to Swift's ..<), ^.. (lower bound excluded, no Swift equivalent), and ^..^ (both bounds excluded, no Swift equivalent).

Current Swift 3 plans include updates to Range that subsume the functions of Interval. A Range in Swift 3 will not necessarily represent a discrete sequence of values but could also represent a continuous interval with floating point bounds. For two integer values a and b, a ... b == a ..< (b + 1). By contrast, for two floating point values c and d, the closed range c...d cannot trivially be expressed as a half-open range without breaking the abstraction that floating point types are intended to represent real values.

For numerical purposes in the educational setting or in the context of scientific computing (among other use cases), it is sometimes necessary to represent intervals where the lower bound, upper bound, or both are open. Although Range offers methods such as contains and clamp applicable to all intervals, Range types only exist at the moment that represent intervals including the lower bound. Concordantly, only the operators ... and ..< exist in Swift. These operators are sufficient (as is any one of the two) for representing sequences of discrete values but they are insufficient for expressing all types of bounded ranges once floating point types (and, in the future, rationals, pattern matching, etc.) are included.

Detailed Design

Adopt the operators <.. and <.< to represent ranges that exclude the lower bound or both bounds, respectively. The correspondingly named types (or, alternatively, a single Range type with Bool properties to indicate bound inclusion or exclusion) must accompany the introduction of these operators.

operator meaning Swift 3 associated protocol (Swift 3 and/or proposed)
... low <= x <= high yes ClosedRangeProtocol
..< low <= x < high yes HalfOpenRangeProtocolClosedOpenRangeProtocol
<.. low < x <= high proposed OpenClosedRangeProtocol
<.< low < x < high proposed OpenRangeProtocol

Sample use cases

Current:

// Simulated data.
func randomValue() -> Double {
    return Double(arc4random()) / Double(UInt32.max)
}
func randomData() -> [Double] {
    return (0..<arc4random_uniform(1000)).map { _ in
        randomValue()
    }
}
let rawData = randomData()

// Now, filter the data to include only those in the desired ranges.
let filteredData0 = rawData.filter((0.0..<0.2).contains)
let filteredData1 = rawData.filter { $0 > 0.4 && $0 <= 0.6 }

Proposed:

// Simulated data.
func randomValue() -> Double {
    return Double(arc4random()) / Double(UInt32.max)
}
func randomData() -> [Double] {
    return (0..<arc4random_uniform(1000)).map { _ in
        randomValue()
    }
}
let rawData = randomData()

// Now, filter the data to include only those in the desired ranges.
let filteredData0 = rawData.filter((0.0..<0.2).contains)
let filteredData1 = rawData.filter((0.4<..0.6).contains)

Current:

for x in [0, 1, 2, 3, // 1st case
          4, 5, // 2nd case
          8, 9, // 3rd case
          11, // 4th case
          6, 7, 10, 12 // no match
          ] {
    switch x {
    case 0...3:
        print("0 <= \(x) <= 3")
    case 4..<6:
        print("4 <= \(x) < 6")
    case let y where y > 7 && y <= 9:
        print("7 < \(x) <= 9")
    case let y where y > 10 && y < 12:
        print("10 < \(x) < 12")
    default: print("No match for \(x)")
    }
}

Proposed:

for x in [0, 1, 2, 3, // 1st case
          4, 5, // 2nd case
          8, 9, // 3rd case
          11, // 4th case
          6, 7, 10, 12 // no match
          ] {
    switch x {
    case 0...3:
        print("0 <= \(x) <= 3")
    case 4..<6:
        print("4 <= \(x) < 6")
    case 7<..9:
        print("7 < \(x) <= 9")
    case 10<.<12:
        print("10 < \(x) < 12")
    default: print("No match for \(x)")
    }
}

Design concerns

This proposal may require that Swift's current operator naming rules be revised with regard to embedded dots. Under the current rules, embedded dots are disallowed in custom operators. Although these new operators would not be "custom" and the rule might not apply, its certainly an issue that would need special consideration in expanding the language and adapting the compiler:

You can also define custom operators that begin with a dot (.). These operators are can contain additional dots such as .+.. If an operator doesn’t begin with a dot, it can’t contain a dot elsewhere. For example, +.+ is treated as the + operator followed by the .+ operator. cite: Swift Programming Language

Alternatives Considered

None

Acknowlegements

Thanks, Dave Abrahams

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