Swift の Conditional conformances(条件付き適合)

この記事はSwift Advent Calendar 2016の24日目の記事です。 Swift4で導入されるConditional conformances(条件付き適合)について調べてみました。

Conditional conformances(条件付き適合)とは

Swift Evolutionでは、今後のSwift向けのいろいろな機能が検討されています。 Xcode8がでるまでは、Swift3向けの機能でもりあがっていましたが、Swift3もおちついてきて、Swift4向けの機能の検討にうつってきています。 先日、SE-0143 Conditional conformances がSwift4にむけて承認されました。

Conditional conformancesとはなんでしょうか。 Genericsはプロトコルに適合できませんが、これを特定の条件下でできるようにしたのが、Conditional conformancesです。

例えば、下記のようなArrayを作ります。 ArrayのElementがEquatableの場合、ArrayもEquatableにしたい場合があります。

下記のようにArrayのArrayの==演算をしようとしても、通常はエラーとなります。

let a1: Array<Int> = [1,2,3,4,5]
let a2: Array<Int> = [1,2,3,4,5]

let diffBetweenArray = (a1 == a2)
let diffBetweenArryOfArray = ([a1] == [a2]) // Error!

Array自体にEquatableを適合させようとしても、Swift3.0ではエラーとなってしまいます。

// Error!!
extension Array:Equatable where Element: Equatable {
    static func == (lhs: Array<Element>,rhs: Array<Element>) -> Bool {
        return .....
    }
}

Conditional conformances(条件付き適合)が採用されると、例えば、Arrayは、ElementがEquqtableに適合していた場合、Equatableに適合することができるようになります。

Arrayで==が使えるけど?

Arrayで==の演算ができているのはArrayがEquatableに適合しているからではなく、下記の演算子が実装されているからです。

/// Returns `true` if these arrays contain the same elements.
public func ==<Element : Equatable>(lhs: ContiguousArray<Element>, rhs: ContiguousArray<Element>) -> Bool

ジェネリクスの動的な型チェック

ジェネリクスがプロトコルに適合することで、動的な型チェックも可能となります。

下記はSE-0143にあげてあった例ですが、プロトコルPに適合しているかどうかでArrayの型チェックを行うことができます。 これは意外に実プロジェクトで役にたちそうな気がしますね。

protocol P {
  func doSomething()
}

struct S: P {
  func doSomething() { print("S") }
}

// Array conforms to P if it's element type conforms to P
extension Array: P where Element: P {
  func doSomething() {
    for value in self {
      value.doSomething()
    }
  }
}

// Dynamically query and use conformance to P.
func doSomethingIfP(_ value: Any) {
  if let p = value as? P {
    p.doSomething()
  } else {
    print("Not a P")
  }
}

doSomethingIfP([S(), S(), S()]) // prints "S" three times
doSomethingIfP([1, 2, 3])       // prints "Not a P"

これからのジェネリクス

Conditional conformancesや今後のジェネリクスについては、Swiftのジェネリクスの設計方針(Generics manifesto)にも書いてあります。

このConditional conformancesについては、Swift4で導入される予定です。