パターンマッチング「func ~=」のデフォルト実装は「func ==」
TL;DR
パターンマッチングのルールを自分で定義するときは、~=
演算子、つまり「func ~=」を定義することになるわけですが、未定義の場合のデフォルト実装は「func ==」を使うようになっている、という話です。そして、NSObject
のデフォルトはisEqual()
が使われる、という話です。
Swiftのパターンマッチング
Swiftでオブジェクトを使ったパターンマッチングをする場合、次のように書きます。
struct A {} let a = A() let b = A() switch a { case a: print("a") // → a case b: print("b") defalut: print("default") }
このときのパターンマッチングに使われるのは、「func ~=
」なる演算子です。この演算子がBool型を返すことによってパターンマッチするかどうかが決まります。
それでは、目的の型が~=
を用意していなかった場合にはどのようになるのでしょうか。この場合、デフォルト処理が使われることになり、「func ==
」が使われます。==
の結果を、~=
の結果として利用しているのです。そして、もしユーザーやライブラリで独自の~=
を定義すると、そちらが使われます。
ケース | 使われる処理 |
---|---|
何も定義していない | デフォルト実装の~= を使う。実態は== |
~=を定義 | 定義した~= を使う |
==だけ定義 | ~= のデフォルト実装から定義した== を使う |
この振る舞いはドキュメントにもきちんと書いてあり、The Swift Programming Languageの[Language References]-[Expression Pattern]にて以下のように書かれています。
By default, the ~= operator compares two values of the same type using the == operator.
それでは実装はどうかというと、swiftソースの stdlib/public/core/Policy.swift
を見ると次のように定義されていて、ドキュメントのとおりになっているとわかります。
//===----------------------------------------------------------------------===// // Standard pattern matching forms //===----------------------------------------------------------------------===// // Equatable types can be matched in patterns by value equality. @_transparent @warn_unused_result public func ~= <T : Equatable> (a: T, b: T) -> Bool { return a == b }
以上です。
ちなみにこのstdlib/public/core/Policy.swift
、他にもいろいろな定義がありとてもおもしろいソースファイルになっています。ぜひ一度読んでみることをおすすめします。
なぜ調べてみたのか
せっかくなので、なぜこんなことを調べたかも記録に残しておきます。
NSObject
を継承したクラスでパターンマッチングをしていたところ、マッチングが行われるたびにNSObject
のisEqual()
メソッドが呼ばれていることに気づきました。
class Foo: NSObject { override func isEqual(object: AnyObject?) -> Bool { return super.isEqual(object) // ←ここにbreakpointを貼ると止まる } } let a = Foo() switch a { case a: print("a") // a default: break }
これはなぜだろう、isEqual()
と~=
とは、どのようにして繋がっているのだろうということが気になり、調べてみたというのがことの発端です。
stdlib/public/SDK/ObjectiveC/ObjectiveC.swift
の末尾で、
// NSObject implements Equatable's == as -[NSObject isEqual:] // NSObject implements Hashable's hashValue() as -[NSObject hash] // FIXME: what about NSObjectProtocol? extension NSObject : Equatable, Hashable { : @warn_unused_result public func == (lhs: NSObject, rhs: NSObject) -> Bool { return lhs.isEqual(rhs) } :
となっていて、==
→ isEqual()
と呼ばれていました。