Swiftで幽霊型を実装する

# 概要
Swiftで幽霊型を実装する方法を説明します。

※幽霊型は英語でPhantom Type（ファントムタイプ）と呼ばれますが、本記事内では幽霊型で統一します。

##  幽霊型とは

幽霊型はSwiftのclass、struct、enumのような**言語機能として提供される型ではありません**。

幽霊型は**実装パターンの一種**です。実装パターンのため、Swiftに限定されるものではなく、Scala、Haskellなどでも同様の表現があります。（参考: [Phantom Typeの紹介](https://dev.oro.com/posts/2017/07/programming/phantom-type/)）

**コンパイル時にしか存在しない**という特徴を持つため、幽霊型と呼ばれるようです。

## 幽霊型の特徴

幽霊型は以下のような特徴を持ちます。

 - 型に型パラメータとして付加情報を持たせることにより、コンパイラでの**静的チェックを強化**する
 - 型による制約がコードに表現されるため、コードが自己文書化され、可読性も向上する
 - 幽霊型の実装はジェネリクスの型パラメータにすぎないため、**実行時には影響しない**

## もっとも簡単な実装例

幽霊型のミニマムな実装例はこれだけです。ここでenumで表現している理由は、**幽霊型はインスタンス化する必要がない**からです。caseを持たないenumはインスタンス化することができないという一般的なテクニックを利用しています。

```swift
enum ATag {}
enum BTag {}

struct Hoge<T> { }
```

型引数により、Aという情報を付与できています。
例えば以下のような実装により、ATag情報を持つHogeインスタンスのみを受け入れるような関数を作成できます。

```swift
func print(hoge: Hoge<ATag>) {
    print("ATagのみ出力")
}

let hoge = Hoge<BTag>()
print(hoge: hoge) // Extraneous argument label 'hoge:' in call
```

## もう少し面白い実装例

以下のような`Cat`型を考えます。

```swift
struct Cat {}
```

幽霊型によって**ねこが空腹かどうかの付加情報を表現**してみます。

まず、`Cat`型に型パラメータ`T`を持たせ、ねこの状態をenumで表現します。

```swift
// ジェネリック型に変更する
struct Cat<CatState> {}

// ねこの状態を表す幽霊型
protocol CatState {}
protocol Runnable {}
protocol Eatable {}
enum HungryCat: CatState, Eatable {} // 空腹
enum NotHungryCat: CatState, Runnable {} // 満腹
```

これでねこの状態を型情報に持たせることができるようになりました。`catA`インスタンスは空腹で、`catB`インスタンスは満腹であることがコンパイラにも理解できるようになります。

```swift
let catA = Cat<HungryCat>()
let catB = Cat<NotHungryCat>()
```

これを利用し、`Cat`型を型制約付きのエクステンションを実装することで幽霊型に応じた実装が可能です。

```swift
extension Cat where T: Eatable {
    func eat() {
        print("食べる")
    }
}

extension Cat where T: Runnable {
    func run() {
        print("走る")
    }
}

let catA = Cat<HungryCat>()
catA.eat()
catA.run() // Type 'HungryCat' does not conform to protocol 'Runnable'

let catB = Cat<NotHungryCat>()
catB.eat() // Type 'NotHungryCat' does not conform to protocol 'Eatable'
catB.run()
```

## より実践的な実装例

[【Swift】型を使うという意味を考える (Phantom Typeを通して) - Qiita](https://qiita.com/shiz/items/1d5a292865fa10ead673)

こちらの記事では幽霊型をショッピングアプリケーションでの**状態管理**に利用したり、UserのidやDogのidなどを同じString型で表現せず、**複数のIDを幽霊型を利用して識別**する方法が示されています。

## 幽霊型を利用しない場合

逆に幽霊型を利用しないパターンも容易に想像がつきます。例えば、先述のHungryCat、NotHungryCatを個別のstructで定義する場合です。

```swift
struct HungryCat: Eatable {}
struct NotHungryCat: Runnable {}
```

それぞれの型の固有の振る舞いはそれぞれの型に定義すれば良いですが、共通の振る舞いを安直に実装すると、**冗長なコード**になります。（`name`プロパティや`sleep()`メソッド）

```swift
struct HungryCat: Eatable {
    let name: String

    func sleep() {
        print("ねる")
    }
}

struct NotHungryCat: Runnable {
    let name: String

    func sleep() {
        print("ねる")
    }
}
```

メソッドであればプロトコルエクステンションでの処理の共通化が可能ですが、プロパティはスーパークラスを定義しないと共通化はできません。（スーパークラスの設計の難しさや構造体からクラスに変更する必要が出てくる）

```swift
extension Sleepable {
    func sleep() {
        print("ねる")
    }
}

struct HungryCat: Eatable, Sleepable {
    let name: String
}

struct NotHungryCat: Runnable, Sleepable {
    let name: String
}
```

上記のように、独自型を用いるのか、幽霊型を用いて既存の型を拡張していくのかどちらが良いのかは修正による影響範囲なども関係するため、ケースバイケースかと思います。独自型と幽霊型の使い分けなどあればコメントに書いていただけるとうれしいです。

# 参考

 - [Swift で Phantom Type (幽霊型) - Qiita](https://qiita.com/taketo1024/items/71e3272211f08d7e0cde)
 - [iOSDC 2017 でさらっと出てきた Phantom Type さらっとやった話 - Qiita](https://qiita.com/Biacco/items/8198e0da6b89d7903079)
 - [Phantom Typeでコンパイル時に状態チェックする: shibuya.swift #4 - speakerdeck](https://speakerdeck.com/kazuhiro4949/phantom-typedekonpairushi-nizhuang-tai-tietukusuru-shibuya-dot-swift-number-4)
 - [FlowtypeでPhantom Typeを楽しむ - Qiita](https://qiita.com/kinzal/items/2c99cdf45a457383ac08)
 - [で、出たー！幽霊型だー！(Phantom Type) - Qiita](https://qiita.com/HirotoShioi/items/3444e215070144b8ca0f)
 - [F(by) 2017. Denis Shevchenko - The Way to Simplicity: How Haskell Simplifies Code Maintenance.](https://www.youtube.com/watch?v=cyOvlaweUtw&feature=youtu.be)
 - [Phantom type - HaskellWiki](https://wiki.haskell.org/Phantom_type)