次のようなコードがあります。
protocol P {}
struct S: P {}
extension Array {
func hoge() -> String { return "Any" } // ①
}
extension Array where Element == S {
func hoge() -> String { return "Equal" } // ②
}
extension Array where Element: P {
func hoge() -> String { return "Protocol" } // ③
}
extension Array where Element == P {
func hoge() -> String { return "Existence" } // ④
}
問題 1
次のコードで呼ばれるのは、メソッド①〜④のどれでしょうか?
[S()].hoge()
問題 2 〜 4
それでは、残りの3つのメソッドを呼び分けるにはどうしたら良いでしょうか?
各々1行から2行のコードで回答してください。
thinking time! ⏱
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
解答
解答 1
[S()].hoge()
上記のコードで呼ばれるのは…
extension Array {
func hoge() -> String { return "Any" } // ①
}
extension Array where Element == S {
func hoge() -> String { return "Equal" } // ②
}
extension Array where Element: P {
func hoge() -> String { return "Protocol" } // ③
}
extension Array where Element == P {
func hoge() -> String { return "Existence" } // ④
}
のうち、②でした。
本記事のコードを
swift -frontend -typecheck -debug-constraints hoge.swift
に掛けてみると、解決のログが確認できました。
ログによると [S()].hoge()
の .hoge()
は ② <Element where Element == S> (Array<Element>) -> () -> Int
の引数側と
Constraint restrictions:
Array<S> to Array<S> is [deep equality]
という関係であるため、他の(サブタイプ関係の)オーバーロードよりも優先的に選ばれたようです。
なお、Swift実装の詳細について @ukitaka さんにご助言いただきました。ありがとうございました。
debug-constraintsの出力みてみると、オーバーロードのどの候補を使うかの比較自体はこの辺りでやってそうな雰囲気ありますね
https://github.com/apple/swift/blob/master/lib/Sema/CSRanking.cpp#L387-L725
解答 2 〜 4
オーバーロードされたメソッド①〜④それぞれを呼び分けるコードは、以下の通りです。
①
extension Array {
func hoge() -> String { return "Any" }
}
[S() as Any].hoge() // "Any"
解説:
extension Array に whereが省略されている場合、 where Element: Any
と解釈される。
その証拠に extension Array where Element: Any
で hoge()
を実装しようとすると redeclaration だって怒られます。
②
extension Array where Element == S {
func hoge() -> String { return "Equal" }
}
[S()].hoge() // "Equal"
解説:
解答1のとおり。
== S
は「S
型(のサブタイプ)である」という意味。
③
extension Array where Element: P {
func hoge() -> String { return "Protocol" }
}
func f<T: P>(_ t: T) -> String { return [t].hoge() }
f(S()) // "Protocol"
解説:
: P
は「P
プロトコルに適合している」という意味。
Pプロトコルとして見てほしければジェネリックな関数を通す(全称量化する)。
詳しくは https://github.com/apple/swift/blob/master/docs/TypeChecker.rst#polymorphic-types
④
extension Array where Element == P {
func hoge() -> String { return "Existence" }
}
[S() as P].hoge() // "Existence"
解説:
== P
は「Pプロトコルの存在型(のサブタイプ)である」という意味。
存在型については 型システムの理論からみるSwiftの存在型(Existential Type) - Qiitaを参照して下さい。
as P
したときには、存在型にパッケージングされる(これを存在量化という)。
存在型Pは、Pプロトコルには適合しない。
詳しい理屈は Swiftでprotocol型の値がそのprotocol自身にconformしていない理由 - Qiita を参照してください。