【実務・中級編】Hackの『Union Types』の網羅的チェックとパターンマッチング:switch文による型安全な分岐処理 – Hack言語 コア・静的型システムとHHVMのアーキテクチャ解析バイブル

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`を戻り値にするだろう。この時、返り値を受け取った側が必ずこの`switch`文を通るように強制することで、非同期処理に伴う「状態の不整合」を劇的に減らせる。

—

結論:型チェッカーと握手せよ

優秀なエンジニアは、コードを書く時間よりも「型を定義する時間」に重きを置く。

Union Typesと網羅性チェックを使いこなすことは、単なる構文の習得ではない。君たちのロジックを、「実行してみないと分からない」という不確実な世界から、「コンパイラが正しいと保証する」堅牢な世界へと引き上げる行為なのだ。

明日からのコードベースで、`if-else`の羅列を見つけたら思い出してほしい。その分岐は、本当に型システムで表現できないのか? と。

君たちのコードが、HHVMの上で完璧な整合性を持って動くことを期待している。

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