Edited at

Swift で Phantom Type (幽霊型)

More than 3 years have passed since last update.

社内の「Scala 勉強会」で Phantom Type (幽霊型) という厨二心をくすぐる感じのデザインパターンを教えてもらったので、同じことを Swift でもやってみました。

インスタンスの状態を変数ではなく 型パラメータ として持つことで、状態チェックを実行時ではなく コンパイル時 に行えるというイカしたテクニックです。

class Status{}

class NotReady: Status{}
class Ready: Status{}

class Something<T: Status> {
static func createInstance() -> Something<NotReady> {
return Something<NotReady>()
}

func readify() -> Something<Ready> {
return Something<Ready>()
}
}

extension Something where T: Ready {
func shout() {
print("phantom types are awesome!")
}
}

StatusNotReady, Ready をサブクラスに持つだけの空クラス(これが幽霊型)で、SomethingStatus 型の型パラメータ T を持っています。Something には where T: Ready という 型パラメータ制約付きの拡張 によって、shout() という関数を追加しています。

createInstance()Something<NotReady> インスタンスが作られ、readify() を呼ぶと新たに Something<Ready> インスタンスが作られます。そうして作られた T: Ready のインスタンスに対してのみ shout() を呼ぶコードが書けるのです。

やってみましょう:

let s = Something.createInstance()

s.shout() // error: 'NotReady' is not a subtype of 'Ready'

ちゃんと コンパイルエラー が出ました!

一方で readify() を呼ぶと…

let s = Something.createInstance()

s.readify().shout() // phantom types are awesome!

エラーもなく、ちゃんと実行もできました!

こうすることで コンパイラレベル で間違った状態での処理を防げるので、そのためのテストも作らなくて済むことになります。

Phantom Type カッコイイですね :trollface::thumbsup: