Swiftの型の限界を超える

これは[Swift Tweets](https://swift-tweets.github.io/)の発表をまとめたものです。イベントのスポンサーとして Qiita に許可をいただいた上で、このような形（ツイートの引用）で投稿しています。

<blockquote class="twitter-tweet"><p dir="ltr">こんばんは。たるのんです。博多でSwiftを書いているなめくじです。美しい型とコードを愛しています。RxSwiftにちょっとだけコミットしています。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820272355230425088"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">Swiftはとても柔軟かつ厳格な型の宣言が可能な言語です。上手に型を使いこなすことで、美しいプログラムの世界を実現することが出来ます。しかし、しばしばコンパイル出来ないプログラムを書いてしまうことがあると思います。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820272480963076097"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">思い描いた美しい世界をあと一歩のところで実現できなかった。今日は、Swiftがコンパイル出来ない型とその原因、そして、それに対するWorkaroundを幾つか紹介します。幾つかの例では具体的なユースケースも合わせて紹介します。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820272606934749185"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">1. Sequence型の変数を作りたい #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820272745254506496"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">Swiftではassociatedtypeを含んだprotooclの変数を作ることが出来ません。SequenceもIteratorとSubSequenceの宣言があるため、変数として作ることは出来ません。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820272883666591745"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">これに対するWorkaroundはとても有名で、type-erasureと呼ばれています。protocolが持つ全ての機能を変数として宣言することで、動作はそのままに、一つのstructとして表現することができます。 #swtws

https://gist.github.com/c828bdf9b7b015172ac566789d50fa5b</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820273019318718466"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">Sequenceのtype-erasureはAnySequenceで、Swift標準ライブラリの中で提供されているので簡単に挙動も確かめることができます。
type-erasureは広く使われていますが、万能では無く、あくまでもWorkaroundです。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820273145063931908"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">type-erasureは任意のprotocolをstructとして実体化したものです。もしprotocolがstaticな変数や関数を持ってしまっている場合は、それらは使うことができません。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820273270989590528"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">また、type-erasureにprotocolを渡した場合、元々の型としての情報は、共通のprotocolとGenericsを残して全て消滅してしまいます。例えば型によって分岐するようなプログラムは、動かないでしょう。(最もそれ自体はあまり良くないですが) #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820273409707753472"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">associatedtypeを持ったprotocolが変数として利用できないのは、エラーメッセージ(can only be used as a generic constraint)からも読める通り、今のところは言語仕様です。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820273547780030467"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">しかし、GenericsManifestoでは記法が言及されていますし、将来のSwiftでは、もしかしたら可能になるかもしれません。
https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generalized-existentials #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820273686313697280"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">日本語では以下の記事が参考になります。より詳しく知りたい方はぜひ。
http://qiita.com/omochimetaru/items/b41e7699ea25a324aefa
http://qiita.com/koher/items/88f6d7a9c9dd2d8c25a7 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820273812256137218"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">2. 循環した型を作りたい #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820273950491955201"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">型Aが型Bを生成し、型Bが型Aを生成する、という循環関係をprotocolで書こうとすると、ある条件下でコンパイル出来なくなります。 #swtws

https://gist.github.com/67cda3c51cc7a9e7c058266ea3784feb</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820274089101144064"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">例えばPaginationRequestはこのパターンに該当します。
前提のRequest型は以下のものを考えます。※参考 APIKit(https://github.com/ishkawa/APIKit) #swtws

https://gist.github.com/0ea7928fd5f8baf48b091450a796979a</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820274214938628096"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">Paginationを以下の3つに分離して作ります。
1. Responseのパース(Requestの具体的な実装)
2. Pagination protocol
3. Paginationの実装 #swtws

https://gist.github.com/51fa5d3e149e1f58e2e4be53dd8b0c06</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820274353082224645"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">このPaginationRequest型はコンパイルすることができません。エラーには、Type alias `Response` circularly references itself. と出てきます。最初に紹介した2番目の例と状況は同じです。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820274491469099008"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">循環を解消しなければ、コンパイルは通りません。循環を解消する方法を考えてみましょう。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820274619118587904"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">PaginatedResponseのGenericsをRequest型で束縛すると循環型ではなくなりますが、Request.Base.Responseが参照出来なくなります。なのでResponseを追加してあげます。 #swtws

https://gist.github.com/9a97e501ffc252988c0ebe08f7a302a5</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820274745086185472"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">これでコンパイルが通るようになります。やりました。

…重複した型をGenericsとして渡すのはなんだか腑に落ちないです。もう少し頑張ってみましょう。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820274870802006016"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">PaginatedResponseがnextRequestとして期待するものは、同じPaginatedResponseを返すRequestの筈。それさえ表現出来ていれば、使う上で困ることは無いでしょう。しかし、Request型をそのまま変数として使うことは出来ない。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820275009239142400"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">我々にはtype-erasureがあります。それを使えば、変数としての表現が可能になりますね。Requestのtype-erasureとして、AnyRequestを考えます。 #swtws

https://gist.github.com/82b380bba2221612fb3965fe1a0cbcc3</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820275135017938944"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">type-erasureを使うことで、循環型を紐解いたPaginationRequestです。 #swtws

https://gist.github.com/4b490f277ce2a93ad233ee0101f9d1af</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820275273459302403"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">やりました。PaginatedResponseのGenericsもResponseだけになって、心なしかスッキリした気がします。つまり何をしたかというと、2つの型で発生していた循環関係を、1つの型に寄せたわけです。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820275411904925697"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">ではこの循環型はどうしてコンパイル出来ないのでしょうか。最初のケースの挙動と、コンパイラの性質から推察するに、恐らく半分は仕様で半分はバグではないかなと考えています。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820275537713053696"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">最初の例でコンパイルが通らなかったのは2番目と4番目の型です。このうち、2番目の型はコンパイルするためにお互いの型が必要です。参照し続けてコンパイルが完了しないのではないでしょうか。一方で4番目の型でコンパイルが通らないのはバグでしょう。どう見ても循環していません。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820275663596756996"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">4番目の型についてはバグ報告があり、masterにマージされている、とあります。来るSwif3.1では、コンパイルできるようになっているかもしれません。
https://bugs.swift.org/browse/SR-3478 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820275802180681732"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">ついでにPaginationRequestもWorkaroundなしで通ってくれたらな~と願ってみたりしますが、ちょっと難しそうな気がします。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820275927930191872"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">3. Generics(associatedtype)のサブタイプを指定したい #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820276053818023936"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">何がやりたいかということの文章化が難しいです。gistを見てください。以下のコードはコンパイルに失敗します。 #swtws

https://gist.github.com/811dc4abe0eb4798a7c5b6fc0e3b4ec5</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820276179533860864"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">UIKitがSwiftyでなくて辛いシチュエーションは無限にあって、Swiftでprotocolを書き直して使うことはよくあると思います。そしてその作業の途中で、先のようなケースに遭遇することが多い。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820276305463623684"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">具体的な例を見ていきましょう。UIViewController Transitioningのカスタマイズは、最も辛いシチュエーションの一つです。これを改善していきたい。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820276431372447745"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">Transitioningが辛いのは、
1. 型が安全ではない(UIViewController?)
2. 状況によってfrom/toが入れ替わる
の組み合わせによって発生しています。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820276557218361346"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">なので、ここを型安全にすればかなり楽になる。(最近は専らHeroが話題ですが今は目を瞑って下さい)
100行ちょっとのProtocol郡です。 #swtws

https://gist.github.com/ac7ae6e2786dd9be36d05a23d3915c02</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820276695521312769"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">使い方は簡単で、AnimatedTransitioningを実装して、UIViewController.presentに渡すだけなのですが、実際に使ってみるとある問題に気が付きます。 #swtws

https://gist.github.com/10fc75da2c9d1dfd4bd04bbbe3bb4fde</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820276821379776512"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">一見全て上手くいくように思えますが、うまくいきません。そしてこのコードは、MyAlertTransitioning.Presenting = ViewControllerにすれば、コンパイルできるようになります。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820276947284463616"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">SwiftではGenerics(もしくはassociatedtype)のサブタイプとしてGenerics funcを制約することは出来ません。ただし、Optionalは例外として指定することなくサブタイプとして振る舞うことが可能です。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820277073260359680"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">先にどうして動かないか、というところの解説をしていきましょう。SwiftのGenericsには、その型においてGenerics型が関数の引数なのか、あるいは変数(または関数の返り値)として振る舞うかについての記述がありません。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820277198976270336"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">class Aとclass B: Aについて、それぞれの型で宣言したものが、お互いに安全かどうかについて考えてみましょう。
A型の変数をB型の宣言で扱うのは危険です。もしかしたら、B型ではないかもしれない。なのでswiftではas!を使うことになります。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820277337212100608"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">対してB型の変数をA型の宣言で扱うのは常に安全です。では関数の引数となった場合はどうでしょうか。
fA=(A)-&gt;()とfB=(B)-&gt;()について考えます。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820277463162843140"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">fA型の関数をfB型の宣言で扱うのは常に安全です。かならず引数にBが入ってくるわけですから、fA型の実装、つまりAとして扱う分に何ら問題はありません。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820277601562300417"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">しかし逆に、fB型の関数を、fA型の関数として扱うのは危険です。引数にはAが入ってくるが、fB型の実装、Bであるとは限らない。
関係性としては、A⊃Bの場合、fB⊃fAとなるわけです。変数と関数の場合に、サブタイプの関係が逆転しているのがわかります。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820277727370428416"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">Genericsでの宣言はAですが、内部の実装としては当然fAがあり得ます。そして仮に下記のようなコードが実現できると、関数内における型の関係について、安全性を証明することはできなくなってしまいます。 #swtws

https://gist.github.com/ac24d30b5e4a9cec68a9c36c3bd15a31</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820277853237309440"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">Generics周りのサブタイプ、Swiftのサブタイプの扱いについては、日本語だと以下の記事がわかりやすいです。ご参考ください。
http://qiita.com/koher/items/972ab84283b2a0d3ce1b #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820277979095801856"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">さて、コンパイルできない原因が解りました。原因がわかって、目の前に動かないコードがある。あとは、Workaroundを作るだけです。1つは、MyAlertTransitionigの実装を変更する方法です。 #swtws

https://gist.github.com/67619c7606909c613c431c64bb4b0a1a</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820278105101078528"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">MyAlertTransitioning自体をGenericsとして作り変える。そうすればMyAlertTransitioningは任意の型についてinitできるようになるので、コンパイルが通るようになります。おそらく最もシンプルかつ強力なWorkaroundです。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820278230963744768"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">2つ目、Optionalと同じ発想で、Genericsのサブタイプを行います。ただし、Optionalはコンパイラの助けを受けて暗黙的に型変換が行われますが、それ以外の型については明示的に書き換える必要があります。 #swtws

https://gist.github.com/f2a9a9622ce53db0c40d08922db54dbd</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820278356708990976"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">このmap関数を使えば、明示的に書く必要があるはものの、コンパイルが通るようになります。
通常意図されるmapと動きや意味が異なるのは許してください。名前を考える時間が無かったんです(発表一時間前の屑) #swtws

https://gist.github.com/b8660ba12ecacacb48fba607c03c4ceb</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820278491673280516"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">3つ目は、Tupleを引数に取るtype-erasureと、パラメータをTuple化するExtensionを作って、AnyAnimatedTransitioning(MyAlertTransitioning().fields)で動くというものを #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820278617531789313"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">考えていたのですが、残念ながらコレは動きませんでした。というのも、SwiftではTupleについては、要素毎のサブタイプはチェックしておらず、以下の最小のコードもコンパイルすることは出来ません。 #swtws

https://gist.github.com/6f064f5819869bad8239f654f8098390</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820278749136465922"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">Argumentは動くじゃないか!と考えるんですが、ArgumentとTupleは別物です。Argumentは1つずつ型がチェックされますが、Tupleが1つずつ型がチェックされることはありません。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820278887586217986"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">じゃあSwift2.0時代にあったArgumentにTupleを与える記法ですが、実はあれもサブタイプはチェックされず、同様のことをしようとするとコンパイルが通りませんでした。
…(´・ω・｀) #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820279025985687552"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">Tupleに関しては、Swift-evolutionのmailinglistに話題があります。前向きな回答ですし、将来のSwiftでは可能になっているかもしれません。(Genericsの改善の方が早そうですがw)
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160215/010363.html #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820279164418682880"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">Conclusion. #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820279302813937664"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">さて、約20分間に渡って、Swiftの型と戯れてきましたがいかがでしたでしょうか?
ツイートに対するコードの配分が多く、追うのが大変だったかと思います。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820279428664016896"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">特に、「3. Generics(associatedtype)のサブタイプを指定したい」は、最初からコードが凶悪で追いつけなかった人が多かったのではないか、と少し反省しています… #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820279564475637760"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">それでも、一連のツイートの中で、過去に諦めた型が実現できそうだ、というものが少しでもあれば、或いは単純に面白いと思っていただけたのであれば、やった価値があったのかなぁと思います。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820279690195701760"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">このセッションの中でわからなかったことや、このセッションで期待してたけど紹介されなかった型があれば、是非リプライやリアル(博多)で会った時に教えていただけると嬉しいです。特に、後者は大好物なので、あれば今すぐにでも考えます。 #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820279816104472577"></a></blockquote>

<blockquote class="twitter-tweet"><p dir="ltr">僕にとってSwiftを書いていて最も楽しい瞬間が、型を作っている時です。同じように、型を作ることが楽しいと思える人が、増えていけば良いなと、願っています。　おわり #swtws</p>&mdash; @tarunon <a href="https://twitter.com/tarunon/status/820279954642378753"></a></blockquote>

## 追記(2017-04-16)

共変性と反変性に関してかなり強力なWorkaroundが出来上がったので追記です。
`3. Generics(associatedtype)のサブタイプを指定したい`について、これでおおよそ解決出来るはず。
https://github.com/tarunon/Covariancable