SwiftOptional四方山話

# はじめに
SwiftのOptionalを取り巻くあれやこれやについて考えを整理しつつ書きます。
多くの人にとっては役に立たない雑多な話題かもしれません。差し当たりSwift EvolutionのDraftのDraftぐらいの話でしょう。つまりほぼ日記です。
ImplicitlyUnwrappedOptional `T!` については話がややこしくなるので割愛します。

# SwiftのOptionalの特徴についてまとめる
## 定義
SwiftにおけるOptionalは以下の定義です。

```swift
enum Optional<T> {
  case some(T)
  case none
}
```
associatedvalueとして任意の型のTを持つ場合`some`と、持たない場合`none`が切り分けられています。
Optionalが存在することで、SwiftはOptional以外の型がnull値を持つことを許していません。一般にnullセーフと呼ばれるプログラミングが可能になります。
https://qiita.com/koher/items/e4835bd429b88809ab33

## `T??`
OptionalにOptionalを持たせるとネストした型として表現できます。例えばT??であれば以下の3パターンを区別することが出来ます。

```swift
let value: Int?? = 1
switch value {
  case .none: print("case1")
  case .some(.none): print("case2")
  case .some(.some(let value)): print("case3")
}
```

例えばOptionalの配列について、firstの結果がnilであった場合に、
1. 配列自体が空だった→`.none`
2. 配列の一番目が`.none`だった→`.some(.none)`
の区別をつけることが出来ます。

## 共変性(covariance)
Swiftではコンパイラによる特別扱いによって、Optionalについて以下の2つの共変性がある程度認められています。
1. `T` is `T?`
2. if `U` is `T` then `U?` is `T?`
### `T` is `T?`
例えば`Int?`で宣言されたフィールドや引数に直接`1`やInt型の変数を渡すことが可能です。
またある値を返す関数オブジェクトを、Optionalの値を返す関数オブジェクトとして暗黙的に変換して渡すことが出来ます。

```swift
func foo() -> Int { return 1 }
let fooOptional: () -> Int? = foo
```

### if `U` is `T` then `U?` is `T?`
Swiftではstandard libralyの幾つかの型についてこの共変性が認められています。
代表的なものはOptional, Array, Dictionaryです。
例えば`Animal`と`Cat`、2つのクラスが継承関係にあるなら、それらのOptionalもまた同様の関係として扱えます。

```swift
class Animal {}
class Cat: Animal {}
let animalOptional: Animal? = Cat?.some(Cat())
```

これは1.との組み合わせも可能で、`[Animal]`は`[Animal?]`の変数に直接代入出来ます。

### 「ある程度」認められている
勿論サブタイプの関係ではないので、型でチェックするとfalseと帰ってきます。

```swift
T.self is T?.Type // false
```

# クイズ

突然ですがクイズです。以下の関数の返す値を予想してください。答えはPlaygroundで確かめてみてください。

```swift
func q() -> Bool {
  let v1: Int?? = nil
  let v2: Int? = nil
  return v1 == v2
}
```

ConditionalConformanceが使える環境であればそのまま実行出来ます(そちらを推奨します)が、Swift4.0.2では出来ないので、以下の関数も実装してください。

```swift
func == <T: Equatable>(lhs: T??, rhs: T??) -> Bool {
    switch (lhs, rhs) {
    case (.some(.some(let l)), .some(.some(let r))): return l == r
    case (.some(.none), .some(.none)), (.none, .none): return true
    default: return false
    }
}
```

さて、結果は予想通りでしたでしょうか?
問題なのは、正解したか不正解したかではなく、この結果がどちらであるかという予想が、どちらも妥当であるという点です。
恐らく正解、不正解の予想は半々になると思っています。

## 解説
関数`q`における`v1`と`v2`の比較において、`Int?`から`Int??`への変換が発生します。この時に何が起こるべきか、という判断で結果が変わります。
### `T`から`T?`への変換が発生するはずだ
`Int?`を`T`に当てはめて考えます。この場合は`.some`でラップすればいいので、`v2`は`.some(.none)`になります。
`v1`は`.none`なので結果はfalseになるはずですね。

### `Int`は`Int?`なので`U?`から`T?`への変換が発生するはずだ
`Int`はそのまま`Int?`として扱えます。ネストしたOptionalだとややこしいので一旦配列で考えてみましょう。
`[1, 2, 3]`を`[Int?]`に代入すると、`[.some(1), .some(2), .some(3)]`になります。
値がなければ、`[]`はそのまま`[]`になりますね。
ということはOptionalでもそのまま`.none`になる、`v2`は`.none`、v1と等しいので結果はtrueになるはずですね。

## じゃ、実際どうなの
結論から言うと、ネストした型の内部で何が起きるかは「わからない」です。どっちにも転びます。コンパイラの気持ちになりましょう。(無理)
一応は報告済みですが、問題の中枢が根深いのでノータッチなんだと思います。
https://bugs.swift.org/browse/SR-6126

# flatMap abusing
クイズは終わり、全く別の話題です。
最近通ったEvolutionの一つに、ArrayのOptional版`flatMap`をリネームしようぜと言うものがありました。
https://github.com/apple/swift-evolution/blob/master/proposals/0187-introduce-filtermap.md

何が問題だったかというと、`(T) -> U`の関数オブジェクトを`(T) -> U?`として変換できてしまうがために、本来`map`を使うべきシーンであっても、`flatMap`が使えてしまう、というものでした。
事実上`flatMap`が`Promise`における`then`相当のものになっていて、それはもう由々しき事態、当然リネームするに至ったわけでした。

Arrayはリネームで解決した格好ですが、`[T]`と`T?`をオーバーロードして別名で`T`を使いたいというユースケースは普通にあって
https://github.com/ReactiveX/RxSwift/blob/e1933ebe219e89021f6ffd4065cfc4fef2c002c7/RxSwift/Deprecated.swift#L9-L36
依然として困った状況は続いています。
`[T]`と`T?`をオーバーロードしたい理由は、これらが`map`/`flatMap`を持っている所謂一般にモナドと呼ばれる類の構造だからです。まとめて取り扱うと捗るシーンが多いんですよね。

# どうすれば良いのか
ここからは僕のTodoです。Evolutionを出さなければいけません。(他の人が出してくれると僕は楽を出来て幸せです)
`T` is `T?`がまずいだろう、という事はわかりやすいのですが、これをコンパイラから消去するとなると影響範囲が無限に大きいので採択はされないでしょう。
誰もが幸せになれないとしても、得られる幸福を最大化しつつ不幸を少なくした提案をしなければいけません。
妥当だろうと思っているのが、ジェネリクスパラメータについては共変性を禁止する、関数オブジェクトについては共変性を禁止するか、警告+明示的変換は無警告、その他変数については、警告+明示的変換は無警告、或いはOptional wrapping operatorを提案するなどを考えていますが、いずれにしてもPRも作らないとEvolutionは提案出来なくなってしまったので、(これはどのタイミングなのかはよくわかっていない)コンパイラのコードと英語とで腐心する毎日です。