[Swift] Rangeの種類が足りない！① 〜演算子との戦い〜

# 概要

Swiftにおいて標準で実装されていない“範囲を表す構造体”を実装してみたよ！
[SwiftRanges](https://github.com/YOCKOW/SwiftRanges)という名前で公開したよ！
演算子と戦ったよ！

# おさらい

Swiftには標準で“範囲を表す構造体”が5種類あります(`Countable*`は除く[^countable-ranges]; また、`UnboundedRange`は[別の記事](https://qiita.com/YOCKOW/items/dd409b9588f4be72f58f)で説明した通り構造体ではありません)。
範囲を表す構造体は次の通り：

* `ClosedRange`: `lower...upper`
* `Range`: `lower..<upper`
* `PartialRangeFrom`: `lower...`
* `PartialRangeThrough`: `...upper`
* `PartialRangeUpTo `: `..<upper`

何が足りない？
…そう、「lowerを含まない範囲」というのが実装されていないのです。~~必要ないからかな？~~

# 足りないものは補おう

というわけで、「lowerを含まない範囲」を実装していきましょう。

* `LeftOpenRange`: lowerは含まないけどupperは含む範囲
* `OpenRange`: lowerもupperも含まない範囲
* `PartialRangeGreaterThan`: lowerを含まず、上限がない範囲

これら3つの構造体を定義するのはとても簡単です。次のようなコードとなりました：

```swift
public struct LeftOpenRange<Bound: Comparable> {
  public let lowerBound: Bound
  public let upperBound: Bound
}

public struct OpenRange<Bound: Comparable> {
  public let lowerBound: Bound
  public let upperBound: Bound
  internal let _isEmpty: Bool
}

public struct PartialRangeGreaterThan<Bound: Comparable> {
  public let lowerBound: Bound
}
```

※`OpenRange`だけ`_isEmpty`とかいうプロパティがあるけど、この記事では気にしない気にしない。

# 演算子を定義しよう 〜実は一筋縄ではいかない〜

たとえば、0以上10未満を表す`Range<Int>`は、`..<`というinfix operatorを用いて`0..<10`というように書くことができます。となると、たとえば0超10以下を表す`LeftOpenRange<Int>`は`0<..10`と書きたくなりませんか？私はなります。
では、さっそく、`<..`というinfix operatorを定義しましょう。

```Swift
infix operator <.. :RangeFormationPrecedence
```

と書けば…

```
error: r.swift:1:17: error: consecutive statements on a line must be separated by ';'
infix operator <.. :RangeFormationPrecedence
                ^
                ;

error: r.swift:1:17: error: operator with postfix spacing cannot start a subexpression
infix operator <.. :RangeFormationPrecedence
                ^

error: r.swift:1:20: error: expected expression
infix operator <.. :RangeFormationPrecedence
```

**…できません！**

なぜ、できないのか？

## 定義できる演算子とは？

SwiftのLanguage Referenceを見てみましょう。[Grammar of operators](https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#//appleref/swift/grammar/operator)を引用します：


> `operator → operator-head operator-characters(opt)`
> `operator → dot-operator-head dot-operator-characters`
> `operator-head → / | = | - | + | ! | * | % | < | > | & | | | ^ | ~ | ?`
> `operator-head → U+00A1–U+00A7`
> `operator-head → U+00A9 or U+00AB`
> `operator-head → U+00AC or U+00AE`
> `operator-head → U+00B0–U+00B1, U+00B6, U+00BB, U+00BF, U+00D7, or U+00F7`
> `operator-head → U+2016–U+2017 or U+2020–U+2027`
> `operator-head → U+2030–U+203E`
> `operator-head → U+2041–U+2053`
> `operator-head → U+2055–U+205E`
> `operator-head → U+2190–U+23FF`
> `operator-head → U+2500–U+2775`
> `operator-head → U+2794–U+2BFF`
> `operator-head → U+2E00–U+2E7F`
> `operator-head → U+3001–U+3003`
> `operator-head → U+3008–U+3030`
> `operator-character → operator-head`
> `operator-character → U+0300–U+036F`
> `operator-character → U+1DC0–U+1DFF`
> `operator-character → U+20D0–U+20FF`
> `operator-character → U+FE00–U+FE0F`
> `operator-character → U+FE20–U+FE2F`
> `operator-character → U+E0100–U+E01EF`
> `operator-characters → operator-character operator-characters(opt)`
> `dot-operator-head → .`
> `dot-operator-character → . | operator-character`
> `dot-operator-characters → dot-operator-character dot-operator-characters(opt)`
> `binary-operator → operator`
> `prefix-operator → operator`
> `postfix-operator → operator`

まず、定義できる`operator`は2種類あって、"`operator-head operator-characters(opt)`"と"`dot-operator-head dot-operator-characters`"です。よく見ると、`operator-character(s)`に`.`は含まれておらず、`dot-operator-head`と`dot-operator-chracter(s)`にのみ`.`は含まれています。
簡単に言うと、**「`.`で始まる演算子以外では2文字目以降に`.`を使えない」**のです[^dot-operator-head]。
結局、`<..`と書くと、`<`と`..`という2つの演算子が並んでいると解釈されてしまいます。だから、上記のようなエラーになってしまったのです。

## 2つの演算子を定義すればいいんだ

Swiftでは`0<..10`と書くと`(0<)..(10)`というように解釈され得ることが分かりました。ということは、`<`というpostfix operatorと`..`というinfix operatorを定義すればうまくいくかもしれません。

まずは`ExcludedLowerBound`という構造体を使って前者を実装することにします：

```swift
public struct ExcludedLowerBound<Bound> where Bound: Comparable {
  public let lowerBound:Bound
  public init(_ lowerBound:Bound) { self.lowerBound = lowerBound }
}

postfix operator <
public postfix func < <T>(_ lowerBound:T) -> ExcludedLowerBound<T> where T: Comparable {
  return ExcludedLowerBound<T>(lowerBound)
}
```

こうすることで、`0<`と書くと`ExcludedLowerBound<Int>(0)`と書くのと同じことになります。
次は、`..`の方の実装です：

```swift
extension LeftOpenRange {
  public init(uncheckedBounds bounds: (lower: Bound, upper: Bound)) {
    self.lowerBound = bounds.lower
    self.upperBound = bounds.upper
  }
}

infix operator ..: RangeFormationPrecedence
public func .. <T>(lhs:ExcludedLowerBound<T>, upper:T) -> LeftOpenRange<T> {
  let lower = lhs.lowerBound
  guard lower <= upper else {
    fatalError("Can't form Range with upperBound < lowerBound")
  }
  return LeftOpenRange(uncheckedBounds:(lower:lower, upper:upper))
}
```

左辺に`ExcludedLowerBound<T>`型、右辺に`T`型をとる、infix operatorとして定義しました。
これで、`0<..10`→`(0<)..(10)`→`ExcludedLowerBound<Int>(0) .. (10)`→`LeftOpenRange<Int>(uncheckedBounds:(lower:0, upper:10))`と同じことになりました！

同じように、`OpenRange`も`PartialRangeGreaterThan`も実装することができます。


## ここで定義した演算子の問題点

前述のように`<`はあくまでpostfix operatorなので、`0 <.. 10`のように`0`と`<`のあいだにスペースを含めることができません(一方で`0< .. 10`はOK)。Swiftの言語仕様上、これが限界です。

# もうちっとだけ続くんじゃ？

これだけ“範囲を表す構造体”が増えると、`AnyRange<Bound>`みたいなtype erasureを実装したくなりますよね。実際に[SwiftRanges](https://github.com/YOCKOW/SwiftRanges)では実装してあります。が、その話はまた別の機会にでも…。

→ 続き: 「[Rangeの種類が足りない！② 〜初めてのtype erasure〜](https://qiita.com/YOCKOW/items/d670a347757d4bc0e812)」


----------

[^countable-ranges]: だって`Countable*`はSwift 4.2からConditional Conformance的な`typealias`になるからね→[PR#13342](https://github.com/apple/swift/pull/13342)

[^dot-operator-head]: `dot-operator-head`は"."のみなので。