Hackの「網羅性」を掌握せよ:Union Typesとパターンマッチングで実現する堅牢なドメインモデリング
Hackの真髄は、実行時の「Maybe」をコンパイル時の「Must」に変換することにある。
多くのエンジニアがPHPから移行してくる際、`mixed`型や動的なチェックに依存したコードを書きがちだ。しかし、HHVMの型チェッカー(`hh_client`)は、単なるコードの補助ツールではない。君たちのロジックの不備を徹底的に暴く、最も厳格なレビュアーだ。
今回は、実務で頻出する「状態遷移」や「APIレスポンスの分岐」を題材に、Union Typesと`switch`文による網羅的チェック(Exhaustiveness Checking)を極める方法を伝授する。
—
1. なぜ「if-else」地獄を捨て、Union Typesを選ぶのか
実務の現場でよく見る、「型を判定して何かをする」コード。
// 悪い例:文字列やマジックナンバーで状態を管理する
public function handle(string $status): void {
if ($status === ‘pending’) { … }
elseif ($status === ‘success’) { … }
// もしここで ‘failed’ が追加されたら? 誰もコンパイルエラーを教えてくれない。
}
これはバグの温床だ。Hackにおける正しいアプローチは、取りうる状態を型として定義し、型チェッカーに全網羅を強制させることである。
—
2. 実装:網羅性を保証する「Type-Safe Pattern Matcher」
以下は、外部API連携などで発生する「処理結果」を厳密に扱うための設計パターンだ。
namespace App\Core;
// 1. 取りうる状態をUnion型で定義
type ApiResponse =
| shape(‘status’ => ‘success’, ‘data’ => string)
| shape(‘status’ => ‘error’, ‘message’ => string)
| shape(‘status’ => ‘loading’);
class ResponseHandler {
public function process(ApiResponse $response): string {
// 2. switch文で型判定を行う
switch ($response[‘status’]) {
case ‘success’:
return ‘Success: ‘ . $response[‘data’];
case ‘error’:
return ‘Error: ‘ . $response[‘message’];
case ‘loading’:
return ‘Still loading…’;
// ここで重要なのは、defaultを書かない、あるいは例外を投げることだ。
// もし新しい状態(例: ‘retrying’)がApiResponseに追加された場合、
// 型チェッカーが「網羅されていないケースがある」と即座に教えてくれる。
}
}
}
なぜこの設計が最強なのか
- コンパイル時の静的解析: 型定義に新しいメンバーを加えた瞬間、コードベース全体で未対応の`switch`箇所が赤く染まる。脳に負荷をかけることなく、変更箇所を特定できる。
- ランタイムのオーバーヘッド最小化: HHVMのJITコンパイラは、この種の`switch`文(特に構造体型に対する)を極めて効率的に最適化する。無駄な`instanceof`判定を繰り返すよりも高速だ。
—
3. 実務で遭遇する「罠」と解決策
「default」ケースの誘惑を断ち切る
`default`句を安易に書くと、網羅性チェックが無効化される。私は、コードレビューにおいて「`default`句が必要な場合は、本当にUnion型で表現すべきロジックか再考せよ」と指摘する。
もし、どうしても網羅しきれない例外的なケースがあるなら、`invariant_violation`(または`assert`)を使い、実行時に「設計の不備」としてクラッシュさせるべきだ。
switch ($response[‘status’]) {
case ‘success’: / … /
case ‘error’: / … /
case ‘loading’: / … /
default:
// 開発中にここへ到達したら、それは我々の設計ミスである
invariant_violation(‘Unknown status encountered: ‘ . $response[‘status’]);
}
非同期処理との相性
非同期API連携を行う場合、`Awaitable
—
結論:型チェッカーと握手せよ
優秀なエンジニアは、コードを書く時間よりも「型を定義する時間」に重きを置く。
Union Typesと網羅性チェックを使いこなすことは、単なる構文の習得ではない。君たちのロジックを、「実行してみないと分からない」という不確実な世界から、「コンパイラが正しいと保証する」堅牢な世界へと引き上げる行為なのだ。
明日からのコードベースで、`if-else`の羅列を見つけたら思い出してほしい。その分岐は、本当に型システムで表現できないのか? と。
君たちのコードが、HHVMの上で完璧な整合性を持って動くことを期待している。