Opaque Result Typeの解説

この記事はこちらの転載です

https://speakerdeck.com/omochi/opaque-result-typefalsejie-shuo

---

## Opaque Result Typeの解説
### omochimetaru
### わいわいswiftc #11

---

# 5秒なら

---

# これを見て

Proposal SE-0244 Opaque Result Type

https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md

---

# これを見て

Swift 5.1 に導入される Opaque Result Type とは何か

https://qiita.com/koher/items/338d2f2d0c4731e3508f

---

# 解説するなら

---

# Opaque Result Type

パフォーマンスを保ちつつ、
真の型を隠蔽する新機能

---

# 動機

---

# 真の型を隠蔽

「あるプロトコルを返す」と言いたいことがある

```swift
func loadUsers() -> ??? // Sequence of User
```

---

具体的な型を返してしまうと将来変更できない

```swift
// ver 1.0.0
func loadUsers() -> [User]

// ver 1.1.0
func loadUsers() -> LazySequence<User>
```

---

# 従来の方法

Type ErasureやExistentialで返す

```swift
func loadUsers() -> AnySequence<User>
```

- 型の正しさを損なう
- パフォーマンスを損なう

---

# 型の正しさを損なう

```swift
let a = AnyCollection<String>(["hello"])
let b = AnyCollection<Character>("hello")
let c = b[a.startIndex]
```

**実行時(!)**エラー

`Collection.Index`に関する型情報が失われている

---

# 書き込み

```swift
protocol P {}
extension Int : P {}

struct Container {
    var value: P {
        get { return _value }
        set { _value = newValue as! Int }
    }
    var _value: Int = 1
}
```

実質的にsuper typeなので、contravarianceポジションで壊れる

---

# パフォーマンスを損なう

ラッパーに包まれてる分遅い

---

# 解決方法

---

# Opaque Result Type

```swift
some P
```

「プロトコルPを満たす**何らかの**型」を表す記法

---

関数の返り値で使える

```swift
extension Int : P {}

func getP() -> some P {
    return 3
}
```

---

プロパティで使える

```swift
var propertyP: some P {
    get { ... }
    set { ... }
}
```

---

associated typeがついてても使える

```swift
func numbers() -> some MutableCollection {
    return [1, 2, 3]
}
var xs = numbers()
let i0 = xs.startIndex
let i1 = xs.index(after: i0)
xs[i0] = xs[i1]
print(xs) // [2, 2, 3]
```

---

# ここがミソ

2つのsome Pは**違う**型

```swift
func f1() -> some P
func f2() -> some P

var a = f1()
let b = f2()
a = b // コンパイルエラー
```

---

ORTは関数単位のアイデンティティを持つ

下記のような状態を想像すると良い

```swift
struct ResultOfF1 : P {}
func f1() -> ResultOfF1

struct ResultOfF2 : P {}
func f2() -> ResultOfF2

var a = f1()
let b = f2()
a = b // コンパイルエラー
```   

---

# ORTの抽象化表現

`some P`として意味付けされるため、将来のバージョンで真の型を変更できる

ソース互換性だけではなく、バイナリ互換性があり、再コンパイルせずに変更できる

```swift
// ver 1.0.0
func f() -> some P { return 3 }

// ver 1.1.0
func f() -> some P { return "3" }
```

---

# 問題の解決をみていく

- 型情報の保持
- 書き込み
- パフォーマンス

---

# 型情報の保持

```swift
func numbers() -> some Collection { return [1, 2, 3] }
func message() -> some Collection { return "hello" }

let a = numbers()
let b = message()
let x = a[b.startIndex] // 型エラー
```

---

# 書き込み可能

```swift
struct S {
    var value: some P {
        get { return _value }
        set { _value = newValue as! Int }
    }
    var _value: Int = 3
}

var s = S()
let x = s.value
s.value = x         // OK
s.value = Int(1)    // Error
s.value = "hello"   // Error
```

---

# パフォーマンス

コンパイラが真の型を知っている時は、最適化により真の型として扱うコードに特殊化されるため、不要なパフォーマンスロスが無い

---

「最適化できるときは速い」という意味ではExistentialと同じ。何が違うのか？

Existentialはsuper typeであるため、「他の型が入っている可能性がない」証明が必要。

ORTは「知らない**ある**型」なので、その証明は不要。単にその真の型を知れば良いだけ。

結果として同一モジュールなら常に最適化可能。

---

# 発展

---

ORTの導入にあたって議論が拡大していった。
収拾をつけるため新しいマニフェストが作られた。

Improving the UI of generics
https://forums.swift.org/t/improving-the-ui-of-generics/22814

Generic Manifestoに続く指針

---

ORTはジェネリクスと似ている

```swift
// Pである真の型を返す
func makeSomeP() -> some P

// Pである真の型Xを受け取る
func useGeneP<X: P>(_ x: X)
```

---

これとは違う

```swift
// Pである真の型Xを返す
func makeGeneP<X: P>() -> X
```

このXは呼び出し側が決める

some Pは呼び出され側が決める

---

# Reverse Generics

呼び出され側が決めるジェネリックパラメータという新概念を考えてみる

```swift
func makeRevGeneP() -> <X: P> X
```

---

ORTはReverse Genericのsugarとみなせる

```swift
// Pである真の型を返す
func makeSomeP() -> some P

// Pである真の型Xを返す
func makeRevGeneP() -> <X: P> X
```

---

Reverse Genericsを使うと、表現力が上がる

```swift
// 2つのsome Pの関連を書けない
func makePS() -> (some P, some P)

// 2つの「同一の」Pを満たす型
func makePS() -> <X: P> (X, X)
```

---

ORT構文の拡張案

```swift
func makePS() 
    -> (some P A, some P B)
    where A == B
```

---

Opaque Result Type → Reverse Generics

?? → Generics

---

# Opaque Argument Type

もしsomeを引数部分に書けたら、Genericsのsugarとみなせる

```swift
// Pである真の型を受け取る
func useSomeP(_ x: some P)

// Pである真の型Xを受け取る
func useGeneP<X: P>(_ x: X)
```

---

```swift
// Pである真の型を受け取る
func useSomeP(_ x: some P)

// Pである真の型Xを受け取る
func useGeneP<X: P>(_ x: X)
```

頭の中で一度`X`を記憶しないので読みやすい、流行りそう

---

# ORTとExistential

よく似ている

```swift
// Pを満たす**とある**型を返す
func makeSomeP() -> some P

// Pを満たす**どんな**型でも代入できる
var b: P = ...
```

---

# Existentialのany記法

```swift
// Pを満たす**とある**型を返す
func makeSomeP() -> some P

// Pを満たす**どんな**型でも代入できる
var b: any P
```

対称的できれい

---

Protocol制約とExistentialの類似

初級者殺し

```swift
// Pは制約
func useGeneP<X: P>(_ x: X)

// PはExistential
func useAnyP(_ x: P)
```

---

違う機能は違う記法のほうがわかりやすい

```swift
// Pは制約
func useGeneP<X: P>(_ x: X)

// PはExistential
func useAnyP(_ x: any P)
```