LoginSignup
8

More than 5 years have passed since last update.

Swiftクイズ: extension Arrayでオーバーロードされたメソッドを呼び分ける

Last updated at Posted at 2018-03-25

次のようなコードがあります。

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: Anyhoge() を実装しようとすると redeclaration だって怒られます。
image.png

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 を参照してください。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8