【入門編】【上級者向け】Phantom Typesによるビジネスロジックの型安全化:コンパイル時に状態遷移を保証する – Hack言語 コア・静的型システムとHHVMのアーキテクチャ解析バイブル

コンパイル時に「ありえない状態」を消し去る:Phantom Typesでビジネスロジックを鉄壁にする

こんにちは。HHVMの深淵を覗き込み、Hackの型システムと日々格闘している皆さんに、一つ上のコードを書くための秘術を伝授します。

多くの開発者が、「実行してみるまでバグがわからない」という地獄を経験したことがあるはずです。特に、ECサイトの注文処理や、複雑なワークフローにおいて「未払いの状態で配送指示を出す」といった「状態の遷移ミス」は、テストコードをどれだけ書いても漏れが生じがちですよね。

今日は、Hackの静的型システムを極限まで活用し、「そもそも不正な状態遷移をコンパイルすら通さない」ようにする魔法、Phantom Types(幽霊型)の概念を解説します。

—

Phantom Typesとは何か?

簡単に言うと、「値そのものは持たないが、型システム上で『今、何の状態か』を記録するためのマーカー」です。

Hackにおいて、クラスや型引数を利用して、「このインスタンスは今どのステージにいるのか」を型レベルで固定します。これにより、間違ったメソッドを呼び出そうとすると、HHVMの型チェッカー(`hh_client`)が容赦なく赤線を引いてくれるようになるのです。

実践:注文状態の遷移を型でロックする

例えば、`Order`クラスを考えます。「未払い(Pending)」から「支払い済み(Paid)」へ遷移し、その後「配送済み(Shipped)」へ至るフローを定義しましょう。

<>
namespace OrderSystem;

// 状態を表すマーカー(インスタンス化はしないので中身は空でOK)
interface OrderStatus {}
final class Pending implements OrderStatus {}
final class Paid implements OrderStatus {}
final class Shipped implements OrderStatus {}

// TStatusに状態を封じ込める
final class Order {
public function __construct(public string $id) {}

// 未払い状態でのみ実行可能
public function pay(this $this): Order {
return new Order($this->id);
}

// 支払い済み状態でのみ実行可能
public function ship(this $this): Order {
return new Order($this->id);
}
}

なぜこれが強力なのか?

このコードの肝は、メソッドのシグネチャにある `this $this` です。
これは、「このメソッドは、型引数が `Pending` であるインスタンスからしか呼び出せない」という制約を強制しています。

例えば、以下のようなコードを書こうとすると……。

function process(Order $order): void {
// $order->pay(); // OK: Pending -> Paid に遷移

$paidOrder = $order->pay();

// $paidOrder->pay();
// ↑ コンパイルエラー!
// 理由: Paid型のインスタンスに対してpay()は定義されていないため。

$paidOrder->ship(); // OK: Paid -> Shipped
}

これこそが、Hackの型システムが提供する最強の防壁です。「ありえない処理」は、コードが実行される前に、ビルドの段階で「存在しないもの」として切り捨てられるのです。

—

陥りやすい罠と解決策

初めて取り組む方がよく躓くポイントを整理しておきましょう。

1. 「インスタンス化してはダメ」
`OrderStatus`の実装クラスを `new Pending()` のように使ってはいけません。これらはあくまで型を識別するための標識です。コンストラクタを `private` にしておくのが賢い設計です。
2. 「型引数の伝播」
`Order` を関数に渡す際、型を曖昧にしてしまうとチェックが機能しません。可能な限り、関数シグネチャでも `Order` のように状態を明示しましょう。
3. 「Like Typesの誘惑」
`~Order`(Like Types)を使いすぎると、型チェックが甘くなります。厳格なビジネスルールを適用したい箇所では、あえてLike Typeを避け、純粋な型を指定してください。

まとめ:Hackを使いこなすということ

Phantom Typesを使うということは、「ビジネス上のルールを、Hackの文法そのものに翻訳する」という行為です。

コードを書くとき、以下の図を頭に浮かべてみてください。

[Pending] –pay()–> [Paid] –ship()–> [Shipped]
^ ^ ^
| | |
型システムがこの遷移以外をすべてブロックしている

このアプローチは、最初は少し窮屈に感じるかもしれません。しかし、大規模なチーム開発や、一度のミスが許されない金融・決済システムのような現場において、これほど信頼できる相棒はいません。

「ここをクリアすれば、Hackの基本はバッチリマスターできた」と言えます。型システムを味方につけ、自信を持ってエディタを閉じられるような堅牢なアーキテクチャを築いていきましょう。

さあ、皆さんのプロジェクトに「幽霊」を召喚して、バグのない世界を構築してください。応援していますよ。

タイトルとURLをコピーしました