Swift 2のProtocol-Oriented Programmingっぽい書き方を理解する

ああそれ古い。

 [SwiftのFunctional Programmingっぽい書き方を理解する]
>
````swift:swift1-way
func isEven(number: Int) -> Bool {
    return number % 2 == 0
}
let evens = Array(1...10).filter(isEven)
print(evens) // [0, 2, 4, 6, 8, 10]
````

[SwiftのFunctional Programmingっぽい書き方を理解する]: http://qiita.com/hanawat/items/c091b5c7f89672a2dbb2

何が古いかというと、いちいち`1...10`を`Array`にしているところ。もうしなくていいんです。そう、Swift 2ならね。

![](https://qiita-image-store.s3.amazonaws.com/0/19227/7751ac6a-9ada-4a10-f3ec-8095382937f3.png)

実際、Swift 2では`.map`はもう`Array`では定義されてないんです。公式ドキュメントの[Array Structure Reference]を見ても、`.map`はどこにも見当たりません。

[Array Structure Reference]: https://developer.apple.com/library/ios/documentation/Swift/Reference/Swift_Array_Structure/

ではどこで定義されているか?

`Array`が準拠している`protocol CollectionType` です。実際に[CollectionType Protocol Reference]を見てみると、`.map`や`.filter`などの定義がごっそりと見当たります。また`CollectionType`には、`Array`のみならず、`(1...10)`の型である`Range`や`Dictionary`も準拠していることもわかります。

[CollectionType Protocol Reference]: https://developer.apple.com/library/ios/documentation/Swift/Reference/Swift_CollectionType_Protocol/

そしてこのprotocolが継承している(親)protocolである`SequenceType`にも同様の定義があることが[SequenceType Protocol Reference]から読み取れます。たとえば`.reduce`は`CollectionType`ではなく`SequenceType`に定義があります。

[SequenceType Protocol Reference]: https://developer.apple.com/library/ios/documentation/Swift/Reference/Swift_SequenceType_Protocol/
ということは`Dictionary`も`.map`できるとこと?

できちゃうんですね、これが。

````swift:dictionary.map
let dict = ["SWIFT":2.0,"XCODE":7.0].map { 
  ($0.0.lowercaseString, $0.1 + 0.1) 
}
// [("swift", 2.1), ("xcode", 7.1)]
````

ただし、見ての通り結果は`[String:Double]`ではなく`[(String,Double)]`と`Array`になってます。余談ですがrubyの`Hash`も同様の挙動を示します。

これが何を意味するか?

* `SequenceType`や`CollectionType`に準拠した型であれば、`.map`や`.filter`は無料で手に入るんです!

* そして`SequeceType`や`CollectionType`にメソッドを追加したら、それに準拠した型にも無料で新メソッドを提供できるんです!

ほんとに?

やりましょう!

````swift:CollectionType#asDictionary
extension CollectionType where Generator.Element:Hashable {
    var asDictionary:[Generator.Element:Generator.Element] {
        // var result = [Generator.Element:Generator.Element]()
        // causes 'ambiguous reference to member "Generator"'
        var result = Dictionary<Generator.Element,Generator.Element>()
        var even = true;
        for idx in self.indices {
            if even {
                result[self[idx]] = self[idx.successor()]
            }
            even = !even
        }
        return result
    }
}
````

Perl の`my %hash = @array`に相当するものですが、うまくいくでしょうか?

````swift
[1,2,3,4,5,6].asDictionary  // [5: 6, 3: 4, 1: 2]
(1...6).asDictionary        // [5: 6, 3: 4, 1: 2]
````

うまく行ったようです。`Array`も`Range`も拡張してないのに、この通り。

とはいえ、現状では問題もいくつか。

* `[Generator.Element:Generator.Element]()`がなぜかエラーを返す。ので`Dictionary<Generator.Element,Generator.Element>()`
* 抽象化されすぎててわかりづらい。例えば`idx`は`Int`ではなく`Self.Index`なので、`idx+1`とかは使えず`idx.successor()`としなければならない。
* `[1:2,3:4,5:6].asDictionary`は期待どおりエラーとなるが、これまたエラーメッセージが"type of expression is ambiguous without more context"と的外れ。`(Key,Value)`が`Hashable`でないのが本当の理由なのに。

とはいえ、最後の問題は`Dictionary`を拡張しておくことで対処できます。

````swift:Dictionary#asDictionary
extension Dictionary {
    var asDictionary:[Key:Value] {
        return self
    }
}
````

各型(types = `struct`, `enum`, `class`)の実装はprotocol extensionより優先されるので。

課題は残れど、Protocol Extensionのおかげで継承のできない`struct`や`enum`でも、OOPにおける親クラスの拡張に相当することが出来るようになったのは画期的です。Protocol-Oriented Programming = POPをAppleが提唱しはじめるぐらいに。WWDC2015の[Protocol-Oriented Programming in Swift]は必見です。

[Protocol-Oriented Programming in Swift]: https://developer.apple.com/videos/play/wwdc2015-408/

実際、[Swift Standard Library Reference]を見てみると、`class`
の外様扱いぶりに驚愕します。Swiftにとって`class`はあくまで`Objective-C`の遺産継承のためにあって、`struct`や`enum`を`protocol`でつないでいくPOPこそがSwift流と言わんがばかりに。

[Swift Standard Library Reference]: https://developer.apple.com/library/ios/documentation/General/Reference/SwiftStandardLibraryReference/

実際のところ、Protocol Extensionを書くのは通常（つまり`class`や`struct`や`enum`）のExtensionよりは気を使いますし、知らなくても動くコードは書けるのですが、少なくとも Swift は Protocol-Oriented Programming Language であるということを知らないと、調べものをするときに困るわけです。

このあたりは、[Software Design]誌の連載の次号記事に書いたのでよろしければ一読を、とステマで締めさせていただきます:-)

[Software Design]: http://gihyo.jp/magazine/SD

Dan the Crusty Swift Programmer