[Swift] 引数の位置のsomeは本当にジェネリック引数なのか？

# 追記
Swift Forumにおける議論の結果、この記事とは異なる決定に落ち着きました。詳細は以下をご覧ください。

https://github.com/apple/swift-evolution/blob/main/proposals/0341-opaque-parameters.md
# 背景

Swift5.1でOpaque Result Typeという種類の型が追加されました。これは「制約を満たすなんらかの特定の型」を関数が返せるようにする仕組みです。その動作は「リバースジェネリクス」という概念で説明されます。

Swiftのジェネリクスの方向性についての議論がなされたForumのスレッド[「ジェネリクスのUIの改善(Improving the UI of generics)」](https://forums.swift.org/t/improving-the-ui-of-generics/22814)では、以下の構文が提案されています。

```Swift
// この2つは同じ動作
func concatenate(a: some Collection, b: some Collection) -> some Collection
func concatenate<T: Collection, U: Collection>(a: T, b: U) -> some Collection
```

また、Opaque Result Typeの導入のプロポーザルである[SE-0244](https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md)でもこの点が言及されています。

> This `some Protocol` sugar [can be generalized to generic arguments and structural positions in return types](https://forums.swift.org/t/improving-the-ui-of-generics/22814#heading--directly-expressing-constraints) in the future


端的にいうと、ここでは`some`というキーワードが次のように働きます。

* 引数の位置ではジェネリックな型
* 戻り値の位置ではOpaque Result Type

この構文は「ジェネリクスの表記の軽量化」という目標を達成します。既存の構文は「威圧感が強い(intimidating)」うえに、慣習的に1文字の名前が利用されるため理解もしづらい傾向にあります。新しい構文は制約を流暢に表現するため威圧感が弱く、無理に名前をつけていないため(匿名型であるため)理解にも支障がありません。

また、`some`の導入時点から意図されてきた`any`というキーワードとの対応も同時に目指されています。`any P`とはプロトコル`P`を用いたExistential Type、すなわち現在`P`と表記される型の新しい表記で、`P`に準拠する任意(any)の型の値を代入できることを示唆する表記です。`any P`への移行は正式な提案には至っていませんが、過去複数のプロポーザルで言及されるなど、半ば既定路線となっています。この`any P`は引数位置でも用いることが出来るので、`some P`も引数位置で用いられると対照的で綺麗です。

この記事では、引数の位置で使われる`some`が本当にジェネリック引数であるのかどうか検討します。

## 根拠付け

戻り値の位置ではOpaque Result Typeを示すキーワードである`some`が引数の位置ではジェネリックな型を表すのは何故でしょうか。非常に直感的に言えば、Opaque Result Typeとジェネリック引数の振る舞いがかなり類似しているからです。

```Swift
let collection: some Collection = "Hello"
print(collection.hasPrefix("H"))    // valueの型がStringなのは分からないのでエラー
print(collection.prefix(3))         // Collectionに準拠した型はprefixを実装しているので呼び出せる

func hoge<T: Collection>(_ collection: T) {
    print(collection.hasPrefix("H"))    // valueの型がStringなのは分からないのでエラー
    print(collection.prefix(3))         // Collectionに準拠した型はprefixを実装しているので呼び出せる
}
```

このため、ジェネリクスを用いたコードを`some`を使って以下のように書くこともできそうです。

```Swift
func hoge(_ collection: some Collection) {
    print(collection.hasPrefix("H")) 
    print(collection.prefix(3))
}
```

ここでは`some`が引数の位置にあります。この引数の位置に置かれた`some P`を指してOpaque Argument Typeと呼ぶ場合があります。

「Opaque」という言葉は、このような状態を形容しています。つまり、ある型が実際に何なのかが不透明である、ということです。そのように考えると、冒頭で紹介したジェネリクスの役割をも担う`some`をもっと単純に表現することが出来ます。つまり「Opaque」であることを標識するものが`some`だということです[^1]。

### Rustとの関連
*私はRustがほとんど分からないので誤りを含む可能性があります。*

これはRustの`impl`の挙動に倣った提案であると考えられます。

Swiftにおけるプロトコルにあたる機能がRustのトレイトです。しかしSwiftと異なり、トレイトをそのまま型として扱うことはできません。`Box<dyn Trait>`として提供されるのがSwiftの`Protocol`にあたり、`impl Trait`によって提供されるのがSwiftのOpaque Result Typeとほぼ同様の働きを持つ機能です[^5]。つまり、`impl Trait`は`Trait`を実装した何らかの決まった型を表していて、その実際の型は分からず、コンパイル時には静的に解決されます。

Rustでは引数の位置に`impl Trait`が現れた場合どうなるのでしょうか。[ピッチ段階にあったOpaque Result Typeのスレッド](https://forums.swift.org/t/opaque-result-types/15645)でこれについての言及があります。

> - We're not even going to mention the use of `opaque` in argument position, because it's a distraction for the purposes of this proposal; see [Rust RFC 1951](https://github.com/rust-lang/rfcs/blob/master/text/1951-expand-impl-trait.md).

ここで言及されているRFC 1951という文章で、ちょうど上記のような内容が主張されています。つまり、引数の位置の`impl Trait`は、ジェネリクスと同様に振る舞う、ということです[^2]。

なお、ちょっとややこしいのですが、Rustでは`impl Trait`がExistential Typeと呼ばれ、Swiftでは`Protocol`がExistential Typeと呼ばれています。この記事で以降Existential Typeという場合はSwiftの意味です。

# 問題
## 発想

さて、現在は許されませんが、そのうち次のような表現が可能になるでしょう。型の構成要素として用いられる`some`です。

```Swift
let someArray: [some Numeric] = [0, 1, 2] // someArray: [Int]
let someClosure: (some Numeric) -> () = { (value: Int) in print(value) } // 実際は (Int) -> ()
```

どちらも特に不思議なことはありません。`someArray`は`Element`がOpaque Result Typeであるような配列で、`someClosure`は`Numeric`に準拠した何らかの型の値を引数に取り、その値を`print`するクロージャです。

脳内で`someClosure`を実行してみましょう。きっと引数の型がOpaqueなので、こんな感じで動くはずです。

```Swift
let number: Int = 42
someClosure(number)  // 引数の型がIntとはわからないのでエラー
someClosure(.zero)   // (some Numeric).zeroは存在するのでエラーにならない
```

とはいえやはり実際に動作しているところを見たいものです。実例を持ってきましょう。

`[some Numeric]`と書けないからといって、そういう型を作ることが出来ないわけではありません。

```Swift
let value: some Numeric = 42
let someArray = [value]   // someArray: [some Numeric]
```

同じことを`(some Numeric) -> ()`でもやってみましょう。`Numeric`だと上手くいかないので、今回は`BinaryInteger`というプロトコルでやってみます。このプロトコルは`isMultiple(of: Self)`というメソッドの実装を要求します。

```Swift
let value: some BinaryInteger = 42
let someClosure = value.isMultiple // someClosure: (some BinaryInteger) -> Bool
```

このクロージャは、脳内で実行したのと全く同じように、引数がOpaqueになっているように動きます。

```Swift
let number: Int = 42
someClosure(number)  // 引数の型がIntとはわからないのでエラー
someClosure(.zero)   // (some BinaryInteger).zeroは存在するのでエラーにならない
```

## 困惑

以上で、`some`を引数の位置に含むクロージャが構成できることを確認し、その挙動は「引数の型をOpaqueにする」と形容できることがわかりました。

ところが、関数ではどうでしょうか。最初に確認したとおり、関数では`some`を引数の位置におくとジェネリックな型を表すのでした。つまり、こういうことが起きます。

```Swift
// 引数の型がOpaqueなクロージャ
let someClosure: (some Numeric) -> () = { (value: Int) in print(value) }
// 引数の型がジェネリックな関数
func someFunction(_ value: some Numeric) { print(value) }

let number: Int = 42
someClosure(number)  // エラー
someFunction(number) // 問題なく動作
```

関数でもクロージャでも、表記が同一であれば型も同じだというのが自然な推測です。しかしここでは、クロージャであるか、関数であるかによって、引数の型が真逆に変わってしまうのです。

うまく理屈をつければこの動作を正当化することはできるでしょう[^3]。ただ、無理に理屈をつけないと説明できない言語仕様は妥当なのでしょうか。

## 別案
`(some Numeric) -> ()`が表すのは、実はジェネリックなクロージャだったかもしれません。こうすることで関数の表現と一致します。

```Swift
let someClosure: (some Numeric) -> () = { (value: some Numeric) in print(value) } 
someClosure(42 as Int)       // 42
someClosure(42.0 as Double)  // 42.0
```

残念ながら、こうしてもやはり不自然な事態が起こります。

以下は全て有効な宣言です。動作に若干の違いはありますが、全て同じように「Opaque Result Type」として動作することが期待されます。

```Swift
let x: some HogeProtocol = Hoge()
var x: some HogeProtocol { return Hoge() }
func x() -> some HogeProtocol { return Hoge() }
```
もちろん以下も同様です。

```Swift
// これはジェネリックなクロージャ
let x: (some Numeric) -> () = /* ... */ 
// なのでこれも
var x: (some Numeric) -> () { /* ... */ }
// これも、ジェネリックなクロージャを返すべき
func x() -> (some Numeric) -> () { /* ... */ }
```

しかし、3つ目はよく考えると奇妙です。下のように`some Numeric`の位置をずらす事を考えると、1つ目はジェネリックな関数、2つ目はジェネリックなクロージャ(Rank2型として)を返す関数、3つ目はOpaque Result Typeを含む関数型になります。

```Swift
func x(some Numeric) -> () -> () { /* ... */ }
func x() -> (some Numeric) -> () { /* ... */ }
func x() -> () -> (some Numeric) { /* ... */ }
```
複雑に考えれば、この振る舞いを理解することは出来ます[^4]。ただ、それではそもそもの目標であった「軽量で読みやすい構文」から程遠いものになってしまいます。

## まとめ
以上をまとめると、引数の位置の`some`をジェネリック引数と考えた場合、次のような問題が生じることになります。

* `(some P) -> ()`型のクロージャを**ジェネリックでない**クロージャと考えた場合
 * 関数宣言と動作が一致せず、非直感的な振る舞いをすることになる
* `(some P) -> ()`型のクロージャを**ジェネリックな**クロージャと考えた場合
 * 振る舞いが非常に複雑になり、理解に支障を来す

どちらにしても、`some P`に関する直感的な理解を諦め、複雑な理由付けや場合分けを用いて納得しなければなりません。コードを書く際にも読む際にも重くのしかかる負担となり、「ジェネリクスのUIの改善」となるどころか、UIが悪化してしまうことになります。

# 解決策

## リバースジェネリクス

リバースジェネリクスという概念がOpaque Result Typeを理解するために提案されています。Opaque Result Typeはリバースジェネリックな戻り値です。

https://forums.swift.org/t/reverse-generics-and-opaque-result-types/21608

リバースジェネリクスを「実装者が型を決めるジェネリクス」と表現することがありますが、より簡潔には「外側へのジェネリクス」と表現することが出来ます。通常のジェネリクスが、外部によって決定される型を内部で利用するという意味で「内側へのジェネリクス」であるのに対し、リバースジェネリクスは内部で決定される型を外部で利用するという意味です。こう表現することで「リバース=裏返し」という言葉がよりうまく当てはまります。

こう考えると、以下のようなコードが多少わかりやすくなります。「実装者」と考えると少し混乱しそうです。

```Swift
// ここから上の行は外側
let value: some Numeric = 42 // この行が内側
// ここから下の行は外側
print(value)
```

## `some`の役割
「内側で見るジェネリックな型」と「外側で見るリバースジェネリックな型」は同一物に見えます。どちらも実際の型は抽象化されているからです。別の言い方にすると「内側で見るジェネリックな型と外側で見るリバースジェネリックな型は共にOpaqueだ」と言えます。「Opaque」と「リバースジェネリック」が同じ意味ではないことに注意してください。ジェネリックな引数も内側から見てOpaqueですが、決してリバースジェネリックではありません。

`some`をジェネリクスに使おう、という提案は「内側で見るジェネリックな型」と「外側で見るリバースジェネリックな型」が同一物に見えることに注目し、これらを`some`で統一しようとするものです。

ところが既に確認したとおり、このような`some`の利用には無理があります。UIの改善とは程遠い複雑性を孕み、解釈もかなり難しくなります。そこで`some`をジェネリクスに使うのを諦め、次のようにすることで無矛盾な統一が得られます。Opaqueか否かに関係なく、リバースジェネリクスにあたるものを`some`で表すのです。

|              | ジェネリクス | リバースジェネリクス |
| ------------ | ------------ | -------------------- |
| 内側から見る | Opaque | `some`(Visible)               |
| 外側から見る | Visible | `some`(Opaque)               |

この場合どうなるのでしょうか。`some P`を引数に取る関数を考えるとき、この引数はジェネリックではなく、リバースジェネリックな型です。`some Numeric`は外部に向かって抽象化された型なので、内部では決まった型として扱えます。この発想を即席の構文で表現すると、下のようになります。

```Swift
// 引数の型がリバースジェネリックな関数
func someFunction(_ value: Int as some Numeric) { 
    print(value) 
}
```

そして、この関数の挙動はクロージャと全く同じになるはずです。つまり以下が成り立ちます。非常に直感的ではないでしょうか。

```Swift
// この2つは同じ動作
let someClosure: (some Numeric) -> () = { (value: Int) in print(value) }
func someFunction(_ value: Int as some Numeric) { print(value) }
```

このとき、`some`の役割はリバースジェネリクスの標識であり、外部への抽象化の宣言です。そして、**Opaqueであることの標識ではありません**。

## ジェネリクスのショートハンド

しかしこれでは冒頭で挙げた`some P`をジェネリクスに導入することのモチベーションであった「ジェネリクスの軽量化」や「`any`との対応」といった目標が達成できなくなってしまいます。そこで発想を転換することでこれまで以上に綺麗な対応関係を持った構文体系を得ることが出来ます。ジェネリクスの標識に`any`を用いるのです。

|              | ジェネリクス | リバースジェネリクス |
| ------------ | ------------ | -------------------- |
| 内側から見る | `any`        | `some`               |
| 外側から見る | `any`        | `some`               |

つまり、こうです。

```Swift
// この2つは同じ動作
func concatenate(a: any Collection, b: any Collection) -> some Collection
func concatenate<T: Collection, U: Collection>(a: T, b: U) -> some Collection
```

この構文の最大の利点は、ジェネリクスとリバースジェネリクスの表記に直接対応していることです。特に、今度は戻り値のジェネリクスにもショートハンドができたことになります。

```Swift
// ジェネリックな型パラメータは常にanyになる
// リバースジェネリックな型パラメータは常にsomeになる
func x<A: P, B: P, ^C: P = Q , ^D: P = Q>(a: A, c: C) -> (b: B, d: D) { /* ... */ }
func x(a: any P, c: Q as some P) -> (b: any P, d: Q as some P) { /* ... */ }
```

こうすることで「軽量化」「直感的な構文」「`any`との対応」「矛盾の解決」といった私たちの欲しかったものが一挙に手に入ります。

# 結論
* **引数の位置の`some`はジェネリック引数ではありません。**
 * `some`はOpaqueではなくリバースジェネリクスの標識に用いた方がいいでしょう。
 * `any`はExistential Typeではなくジェネリクスの標識に用いた方がいいでしょう。

`some`をOpaqueの標識と考えてジェネリック引数に用いれば、大きな混乱を招くことになるでしょう。そこで`some`をリバースジェネリクスの標識と考えることで理解しづらい挙動を取り除くことが出来ます。さらに`any`をジェネリクスの標識として導入することによって綺麗な対応関係、軽量な構文、さらには混乱のない体系という欲しかったものを得られるのです。

# 補足

## これまでの言及

実際のところ、リバースジェネリックな引数という発想は以前にも出てはいます。

例えばChris Lattner氏によるOpaque Type Aliasという提案[^7]ではリバースジェネリックな引数が書ける可能性が提示されていました。

https://gist.github.com/lattner/d285d42ad21ea2672df9532e6d230e59

> You can take opaque types as arguments as well, because the compiler knows the identity of types on the caller side:
>
> ```Swift
> public func extractField(a : OpaqueReturn1) -> Int {
  // I'm defined in the same module as OpaqueReturn1, so I know it is a string.  
  return a.count
}
> ```

これに関連して、Joe Groff氏は以下のように[述べています](https://forums.swift.org/t/se-0244-opaque-result-types/21252/112
)。(時系列は上の提案より前です)。

> Opaque typealiases are completely independent of "opaque argument types". There is perhaps an alternative factoring of these features, where we have opaque typealiases, and then the "some" sugar is introduced uniformly for arguments (to introduce anonymous generic arguments) and returns (to introduce an anonymous opaque return typealias).

面白いことに、長い議論の中でリバースジェネリックな引数についての言及はこれくらいです。需要のなさを物語っているとも思います。

## RFC 1951での言及
記事を書いている最中に気付いて大変驚いたのですが、Opaque Result Typeのピッチやプロポーザルで言及されていたRFC 1951では、私の主張と同様に、`some`がリバースジェネリクス、`any`がジェネリクスに充てられています。

https://github.com/rust-lang/rfcs/blob/master/text/1951-expand-impl-trait.md#universals-any-versus-existentials-some

> In any case, one longstanding proposal for `impl Trait` is to split it into two distinct features: `some Trait` and `any Trait`. Then you'd have:

> ```Rust
// These two are equivalent
fn foo<T: MyTrait>(t: T)
fn foo(t: any MyTrait)
>
// These two are equivalent
fn foo() -> impl Iterator
fn foo() -> some Iterator
>
// These two are equivalent
fn foo<T: Default>() -> T
fn foo() -> any Default
```

`impl Trait`を引数位置に用いてジェネリクスとして使おうと主張するRFC 1951の議論では、`some`と`any`が一貫してリバースジェネリクスとジェネリクスの意味で用いられています。Swiftは**ジェネリクスの`any`とリバースジェネリクスの`some`の両方の特徴を持った概念として説明されていた`impl Trait`を`some Protocol`として導入し、それをジェネリック引数として使おうとしています**。なぜ？

ちなみに、その後のセクションでは以下のようにも書かれています。

> it's possible to make sense of `some Trait` and `any Trait` in arbitrary positions in a function signature. But experience with the language strongly suggests that `some Trait` semantics is virtually never wanted in argument position, and `any Trait` semantics is rarely used in return position.

「リバースジェネリックな引数」「ジェネリックな戻り値」のショートハンドはほとんど需要がない、というこの指摘は実際その通りだと思います。だからこそRustは`some`と`any`ではなく`impl`を導入するだけで済ませたのです。しかし`impl Protocol`でも`opaque Protocol`でもなく`some Protocol`を導入したSwiftでは、もはやこの道しかないのではないでしょうか。

> this RFC also proposes to *disallow* use of `impl Trait` within `Fn` trait sugar or higher-ranked bounds, i.e. to disallow examples like the following:
>
```Rust
fn foo(f: impl Fn(impl SomeTrait) -> impl OtherTrait)
fn bar() -> (impl Fn(impl SomeTrait) -> impl OtherTrait)
```
>
While we will eventually want to allow such uses, it's likely that we'll want to introduce nested universal quantifications (i.e., higher-ranked bounds) in at least some cases; we don't yet have the ability to do so. We can revisit this question later on, once higher-ranked bounds have gained full expressiveness.

面白いことに、`Fn`(クロージャを示すトレイト)内部での`impl Trait`の利用は禁止され、これを高ランクの型に当てるかもしれないということになっています。Swiftで言えば`any ClosureProtocol<.Argument == any P, .Result == Int>`のような書き方ができるという話なので、確かに高ランクの型を意味してもおかしくないかもしれません。

Swiftでもクロージャ内での`some`の利用を禁止できるかもしれません。そうすることで上で私が問題として挙げた不自然な振る舞いは考えずに済むでしょう。ただ、今度はクロージャ内での`some`の利用が禁止されていること自体が理解しづらい例外になってしまいます。

## `Protocol`のための`any`

Existential Typeにつけるためのキーワードがなくなってしまうのが気になるかもしれません。これについては`exist`などのキーワードがつけばいいのではないでしょうか[^6]。

不安点として型消去に用いられる`AnyHogehoge`やトップ型の`Any`が挙げられます。`some`との対応を目指すのであれば多少被ってしまったとしても`any`をジェネリクスに使った方が良さそうだと思います。

## 定数に用いる`any`

`some`が定数の型にも使えることから、`any`も定数の型で使えた方が良さそうです。これに当たる概念は既に[Generics Manifesto](https://github.com/apple/swift/blob/main/docs/GenericsManifesto.md)で言及されています。

> **Generic constants**
`let` constants could be allowed to have generic parameters, such that they produce differently-typed values depending on how they are used. For example, this is particularly useful for named literal values, e.g.,
>
```Swift
let π<T : ExpressibleByFloatLiteral>: T = 3.141592653589793238462643383279502884197169399
```

`any`はちょうどこの宣言と同じ意味になるはずです。つまり以下のように動くでしょう。

```Swift
// この2つは同じ動作
let π<T : ExpressibleByFloatLiteral>: T = 3.141592653589793238462643383279502884197169399
let π: any ExpressibleByFloatLiteral> = 3.141592653589793238462643383279502884197169399
```

これが実現されれば、`some`と`any`の構文上の対称関係がより明確になるはずです。

[^1]: 厳密には「値の**主な**利用者にとってOpaque」と`some`の役割を説明することができます。引数を主に利用するのは関数の内部、戻り値を主に利用するのは関数の外部だからです。
[^2]: ただし、導入には反発も強かったようです。[Add RFC undo-universal-impl-trait. by phaazon · Pull Request #2444 · rust-lang/rfcs](https://github.com/rust-lang/rfcs/pull/2444)
[^3]: 例えば「戻り値の位置の`some`はOpaque Result Typeで捉え、純粋に引数の位置にある`some`のみジェネリクスで捉える」と考えれば辻褄が合います。ただ、かなり複雑です。
[^4]: 例えば「純粋に戻り値の位置の`some`のみOpaque Result Typeで捉え、引数位置にある`some`は**その場で**ジェネリクスと捉える」と考えれば良さそうです。ただ、高ランク型の実装が必要になりますし、直感的とはとても言えない考え方です。
[^5]: そもそも歴史的にはRustの`impl Trait`を参考に導入されたのがSwiftのOpaque Result Typeです。
[^6]: 具体的なキーワードの提案ではありません。
[^7]: Opaque Result Typeの議論の中で、Alternativeとして提案されたものです。