Array や Dictionary にもモダンでオシャレな extension を実装する

# 前書き
[RxSwift](https://github.com/ReactiveX/RxSwift) や [Kingfisher](https://github.com/onevcat/Kingfisher)、そして自分の [Danbo](https://github.com/el-hoshino/Danbo) などのライブラリーに採用されている `.ex` とかで拡張を明確に区別した `extension` の実装法、やり方については[こちらの記事](http://qiita.com/motokiee/items/e8f07c11b88d692b2cc5)や[こちらの記事](http://tech.vasily.jp/entry/swift_modern_extensions)に詳しい実装方法を書かれていますが、それらの方法では `Array` や `Dictionary` といった自分自身の下位型縛りがある型にはそのまま適用できませんでした。`where Base == Array` をかくとコンパイラに怒られます。

[昨日の投稿](http://qiita.com/lovee/items/b53c1b35c65d48182943)に引き続き、どうしても [Eltaso](https://github.com/el-hoshino/Eltaso/tree/feature/Swift_4.0) で `[1, 2, 3].eltaso.random` みたいな書き方をできるようにしたいと思い、昨日色々と四苦八苦した結果、どうにか無事成功したようで、やり方を共有しつつマサカリをお待ちしております。

# 目標
とりあえずこの記事ではより一般受けな書き方を共有したいと思いますので上記のとちょっと違い `[1, 2, 3].ex.random` を目標にしたいと思います。

# 前準備
一応[こちらの記事](http://tech.vasily.jp/entry/swift_modern_extensions)をベースにしてますので、そちらの記事を参照しながらこの記事を読むとわかりやすいかもしれません。また、できればまず `Array` じゃない普通の型にこの実装をする方法を覚えておくことをオススメします。

# 実装
```swift:ExampleCompatible.swift
public protocol ExampleCompatible {
	associatedtype CompatibleType
	var ex: CompatibleType { get }
}

public struct Example<Base> {
	let base: Base
}

public struct ExampleWithSingleAssociatedType<Base, AssociatedType> {
	let base: Base
}
```

```swift:Array.swift
extension Array: ExampleCompatible {
	public var ex: ExampleWithSingleAssociatedType<Array<Element>, Element> {
		return ExampleWithSingleAssociatedType(base: self)
	}
}

extension ExampleWithSingleAssociatedType where Base == Array<AssociatedType> {
	public var random: AssociatedType? {
		guard !self.base.isEmpty else {
			return nil
		}
		let index = Int(arc4random_uniform(UInt32(self.base.count)))
		return self.base[index]
	}
}
```

```swift:Playground.swift
[1, 2, 3].ex.random // 2
```

# 解説
## `ExampleCompatible.swift`
まず `ExampleCompatible` の部分についてみてわかる通り、`Example<Base>` の他に、`ExampleWithSingleAssociatedType<Base, AssociatedType>` というやつを新たに追加しました。通常の `Example` と違い、こちらは `Base` と `AssociatedType` の2つの型縛りがあります。それ以外は `Example` と同じです

また、ここであえて `ExampleCompatible` の `extension` を削除しました。なぜかというと `Array` は `ExampleWithSingleAssociatedType` に入れますが、通常の型は `Example` に入れるので、直接 `extension ExampleCompatible` の中に `ex` を定義してあげると物によっては違う型の `ex` が返されてくる危険性がありますので、次の `Array.swift` の部分と同じように、直接型拡張の中に定義することにしました。

## `Array.swift`
次に `Array.swift` の中ですが、まずは `Array` を拡張して `ExampleCompatible` を適用させます。これは通常の型の拡張を作る時と同じです。ただしここでは直接 `ex` の戻り値を定義する必要があります。戻り値の型はご覧の通り `ExampleWithSingleAssociatedType<Array<Element>, Element>` です。つまり `ExampleWithSingleAssociatedType` の `Base` は `Array<Element>`、`AssociatedType` は `Array.Element` になります。

ここまで定義できたらあとは通常の型の時の `Example` の拡張と同じように、`ExampleWithSingleAssociatedType` を拡張します。制約は `Base == Array<AssociatedType>` になります。

拡張の中に `random: AssociatedType?` というプロパティーを定義します。中身は単純な自分のランダムな要素を返す（ただし空配列の場合は `nil` を返す）だけです。

## `Playground.swift`
ここまでできたらもう `Playground` 開いて `[1, 2, 3].ex.random` を書けば自動で `1`、`2`、`3` の中からランダムな数を返してくれる命令が使えます。ね？簡単でしょ？

# `Array` 以外への適用
上記の `ExampleWithSingleAssociatedType` の定義からわかる通り、`Base` と `AssociatedType` の二つの型縛りがあるので、`Array` だけでなく、自分自身に下位の型縛りが一つある型なら基本どれにでも適用できます。例えば `Range<Bound>` とかですね。書き方は単純に `Array.swift` の中身の `Array` を `Range`、`Element` を `Bound` に書き換えればできるはずです。

では、下位型縛りが二つある物、例えば `Dictionary<Key, Value>` はどうすればいいかというと、これも非常に単純で、さっき `ExampleWithSingleAssociatedType<Base, AssociatedType>` を追加したのと同じように、もう一つ `ExampleWithDualAssociatedType<Base, AssociatedType1, AssociatedType2>` という `struct` を定義して、`Dictionary` の `ex` を `ExampleWithDualAssociatedType<Dictionary<Key, Value>, Key, Value>` で返せば OK です。まあ型縛りが増えてくると書くのがだるいですねw

# 余談
上記はより一般向け（？）な書き方で書きましたが、自分の [Eltaso](https://github.com/el-hoshino/Eltaso/tree/feature/Swift_4.0) では `Example` ではなく `EltasoContainer` で名付けられてたり、拡張は `eltaso` の名前にしてます。せっかくわざわざ名前衝突を回避しようとこんな書き方導入したっていうのに、他の同様な書き方をするライブラリー導入して `ex` で名前衝突したらシャレにならないですもんね。というわけで皆さんも自分でこの書き方を導入する際は `ex` ではなく他の名前で入れた方が無難かと思います。

ちなみにこの拡張方法は Eltaso の 4.0 で一般公開する予定ですが、このバージョンではこれと別にメソッドチェーンの対応も実装しようと考えております（どれほどの実用性があるかはさておき）。仕事量がかなり多いのでもしかすると 4.1 で実装する可能性もありますが、一応現在の対応状況をチョイ見せするとこんな感じです：
![スクリーンショット 2017-06-14 15.44.15.png](https://qiita-image-store.s3.amazonaws.com/0/63998/69a3bd10-9c54-75ae-0b8b-58730281a1fa.png)

パフォーマンス面も思ったよりそんなに悪くないようです：
![スクリーンショット 2017-06-14 15.53.41.png](https://qiita-image-store.s3.amazonaws.com/0/63998/fb98a2bc-d653-bb8e-85f3-ae7890c2b0a0.png)

たーのしー