Type Omission - 呼び出し時に型名を省略できるstatic fieldの話

# 前置き

try! Swiftお疲れ様でした！

[try! Swift 資料まとめ - Qiita](http://qiita.com/mishimay/items/895d676eb9940d75e639) や [niwatakoさんの書き起こし](http://niwatako.hatenablog.jp/) のおかげで、在宅組にもかかわらず沢山の知見を得ることができました。

さて、この記事では

[try! Swift Swiftヒップスター #tryswiftconf Day3-4 - niwatakoのはてなブログ](http://niwatako.hatenablog.jp/entry/2016/03/04/131157)

にある1枚のスライド

```swift

struct PowerLevel {
	let value: Int
	static func determinePowerLevel(_ fighter: ZFighter) -> PowerLevel
}

func powerLevelIsOver9000(powerLevel: PowerLebel) -> Bool {
	return powerLevel.value > 9000
}

//Type omission!
if powerLevelIsOver9000(.determinePowerLevel(goku)) {
	print("It's over 9000!")
}
```

>enumの中の静的変数はenumを返してくる、同じことをstatic　func でやっている。
>static var が enum のように動くなら、タイプを省略できます

について詳しく調べてみようと思います。

# 疑問

「あーね、わかるよ、Enumで `.Hoge` とか書けるのと同じだよね。 `init(frame: CGRect.zero)` を `init(frame: .zero)` って書けるやつだよね」
とあっさり納得しそうになったんですが、よく考えると不思議ですよね。
frameがCGRectなのは自明だから型推測が走る…のは分かる。
どこかの型に `.zero: CGRect` が定義されていることまでは分かるだろうけれど、**定義されているのがCGRect**だということはどうやって判断するんだ？
**Selfを返す場合には省略できる**というルールなのか…？

- Type Omissionは、Selfを返す場合のみ有効？
- Type Omissionは、継承関係のあるclassでも有効なのだろうか？
- Type Omissionは、サブクラスではどう動くのか？

これらの各疑問を、コードとともに検証します。


## 疑問1. Type Omissionは、Selfを返す場合のみ有効？

検証コード

```swift
struct A {
    static var aToA: A { return A() }
    static var aToB: B { return B() }
}

struct B {
    static var bToA: A { return A() }
    static var bToB: B { return B() }
}

func doSomething(a: A) {}

doSomething(A.aToA)
doSomething(B.bToA)
doSomething(.aToA)
doSomething(.bToA) // error: Type 'A' has no member 'bToA'
```

なるほど、4つ目でビルドエラーになりました。

`doSomething` は引数として型 `A` を取ります。
ここでコンパイラは、 `A` に `aToA: A`, `bToA: A` が定義されていることを期待するわけですね。
実際に `bToA: A` が定義されているのは `B` ですが、コンパイラが辿るのはあくまで `A` だけなのだとわかりました。


## 疑問2. Type Omissionは、継承関係のあるclassでも有効なのだろうか？

検証コード
`C` を継承したサブクラス `CC` を作ります。

```swift
class C {
    static var c: C { return C() }
    class var c_inheritable: C { return C() }
}
class CC: C {
    override class var c_inheritable: C { return CC() }
}

func doWithC(c: C) {
    print(c)
}

doWithC(.c) // C
doWithC(.c_inheritable) // C
```

いけましたね！　親まっしぐら。
型として要求されているのは `C` なので、サブクラスがどうなってようとガン無視です！


## 疑問3. Type Omissionは、サブクラスではどう動くのか？

検証コード
`C` を継承したサブクラス `CC1` と `CC2` を作ります。

```swift
class CC1: C {
    override class var c_inheritable: CC1 { return CC1() }
}
class CC2: C {
    override class var c_inheritable: C { return CC2() }
}

func doWithCC1(cc: CC1) {
    print(cc)
}
func doWithCC2(cc: CC2) {
    print(cc)
}

doWithCC1(.c_inheritable) // CC1
doWithCC2(.c_inheritable) // error: type of expression is ambiguous without more context
```

`doWithCC1` では、コンパイラは、求められている型 `CC1` に `c_inheritable: CC1` が定義されているか探します。
`c_inheritable: C` は、継承にあたって、より詳細に `c_inheritable: CC1` と書くことができます。
期待通りfieldが見つかり、無事、コードは解釈されました。

一方、`doWithCC2` では、求められている型 `CC2` に `c_inheritable: CC2` が定義されているか探します。
しかしビルドエラーが発生。なぜなら、定義されているのは `c_inheritable: C` だからです。

```swift
doWithCC2(.c_inheritable as! CC2) // error: type of expression is ambiguous without more context
```

こう書けば通るかも？と思ったんですが、ダメでした。キャストの問題ではなく、fieldの探索の問題ということですね。

# まとめ

- 推測された型に**Selfを返すstatic fieldがある場合**、呼び出し時には型名を省略することができる。
- サブクラスでType Omissionを働かせるためには、戻り値の型もSelfに合わせる必要がある。