やあ、Hackの世界へようこそ。私はこの言語の深淵で、HHVMがどのようにバイトコードをJITコンパイルし、型チェッカーがどのようにメモリの安全性を見守っているのかを追い続けてきた者だ。
PHPという「動的な荒野」から、Hackという「堅牢な要塞」へ。今日君が学ぼうとしているのは、単なる文法の書き換えではない。「エラーを隠蔽する文化」から「エラーを設計の不可欠な要素として扱う文化」へのパラダイムシフトだ。
心して聞いてほしい。例外(Exceptions)に頼るコードは、実は「地雷をどこに埋めたか忘れたまま歩く」のと同じなんだ。さあ、型システムでその地雷を可視化する方法を紐解こう。
—
1. なぜ「例外」はHackの思想にそぐわないのか
PHPの例外は、いわば「突然の天災」だ。関数がどこで失敗するのか、呼び出し元は知る由もない。`try-catch`で囲むのを忘れた瞬間、アプリケーションはクラッシュする。
Hackでは、「エラーは戻り値の一部であるべき」と考える。これを実現するのが `Result
PHP的な「悪しき」エラー処理
// PHPでは失敗時に例外を投げるのが一般的
function getUser(int $id): User {
if (!$exists) {
throw new Exception(“User not found”); // 呼び出し側がこれを覚えている必要がある
}
return $user;
}
Hack的な「正しき」エラー処理(Result型)
use namespace HH\Lib\Result;
// 戻り値が「Userか、それともエラーか」を型レベルで強制する
function getUser(int $id): Result
if (!$exists) {
return Result\err(“User not found”);
}
return Result\ok($user);
}
この違い、わかるかな? 関数定義を見ただけで、「あ、この関数は失敗する可能性があるんだな」と型システムが君に教えてくれる。これがHackの強みだ。
—
2. Result型の基本操作:パターンマッチング
`Result
$result = getUser(123);
// match式ですべてのケースを網羅する(網羅性チェックが働く!)
$output = match ($result is Result\Ok<_>) {
true => “Hello, ” . $result->unwrap()->name,
false => “Error: ” . $result->getErr(),
};
ここがポイント:
もし君が `Result\err` のケースを書き忘れたら、Hackの型チェッカーは容赦なくコンパイルエラーを吐く。「全ての可能性を考慮しろ」という、言語からの厳しくも優しいメッセージだ。
—
3. 初学者が陥りやすい「罠」
この移行を進める際、多くの初心者が踏む地雷が二つある。
① `unwrap()` を安易に使うな
`Result` を強制的に取り出す `unwrap()` メソッドがあるが、これは中身が `Err` だった瞬間に例外を投げる。「例外を避けるためにResultを使っているのに、結局例外を投げている」という本末転倒な状態だ。`unwrap()` は、コードの論理的に「絶対にエラーにならない」と確信できる場所でしか使わないこと。
② 巨大なエラー型を定義しすぎる
`Result
Hackでは `enum` や `shape` を使って、具体的なエラーの種類を定義するのがベストプラクティスだ。
enum UserError: string {
NotFound = ‘NOT_FOUND’;
PermissionDenied = ‘PERMISSION_DENIED’;
}
// これなら型安全にエラーハンドリングが可能
function getUser(): Result
—
4. 伝説のアーキテクトからのアドバイス
PHPからHackへの移行は、最初は窮屈に感じるかもしれない。だが、覚えておいてほしい。「コンパイル時に直面する苦痛は、デバッグ時に直面する絶望より遥かに軽い」ということを。
HHVMの型チェッカーは、君が寝ている間も、コードの隅々まで目を光らせている。Result型を使ってエラーを戻り値に押し込めることは、君のコードに「自己防衛能力」を授けることなんだ。
さあ、恐れずに例外を捨て、型という地図を手に取ろう。ここをクリアすれば、君はもう単なるPHPコーダーではない。「堅牢なソフトウェアを構築するエンジニア」の仲間入りだ。
何か詰まったら、いつでも戻ってくるといい。君のコードが型システムによって美しく洗練されるのを、心から期待しているよ。