Hackの型システムを掌握せよ:共変・反変がもたらす「壊れない」ジェネリクス設計
Hackの静的型システム(Strict Mode)と向き合うとき、多くのエンジニアが「なぜこの代入は許可されないのか?」という壁に突き当たる。その正体こそが、型理論における変位(Variance)だ。
我々がHHVM上で高パフォーマンスかつ堅牢なシステムを構築する際、ジェネリクスの扱いを誤れば、実行時のランタイムエラーを招くか、あるいは型チェッカーによって開発速度を無駄に削がれることになる。
今日は、コンテナ設計やAPIレスポンスのモデリングにおいて避けては通れない「共変・反変」を、実務レベルで完全に制御する方法を伝授する。
—
1. なぜ「型チェッカー」は柔軟性を許さないのか
まず、基本的な前提を共有しよう。`vec
もし `vec
2. 読み込み専用の「共変(Covariance)」: `+T`
データを読み出すだけのコンテナであれば、サブタイプの代入を許可しても安全だ。これが「共変」である。Hackでは `+` を用いて、この特性を型システムに宣言する。
実践:読み取り専用コレクションの設計
<<__ConsistentConstruct>>
abstract class Animal {}
class Dog extends Animal { public function bark(): void { echo “Woof!”; } }
// +T を指定することで、このインターフェースは「読み取り専用」として扱われる
interface IReadOnlyCollection<+T> {
public function get(int $index): T;
}
class DogCollection implements IReadOnlyCollection
public function get(int $index): Dog { return new Dog(); }
}
function processAnimals(IReadOnlyCollection
// +T のおかげで、DogCollection をここへ渡すことが可能になる
$animal = $animals.get(0);
}
// 利用例
$dogs = new DogCollection();
processAnimals($dogs); // 型チェックをパスする
知見: `+T` を付与した型パラメータは、メソッドの引数(入力)には使えない。メソッドの返り値(出力)にのみ使用可能だ。HHVMの型チェッカーは、この制約により「汚染」が起きないことを静的に証明している。
3. 書き込み専用の「反変(Contravariance)」: `-T`
逆に、データを受け取るだけの処理(Sink)においては、親クラスを受け取れる関数は子クラスも受け取れるはずだ。これが「反変」である。
実践:柔軟なログ出力ハンドラ
// -T を指定することで、Animal を扱える処理は Dog も扱えるようになる
interface IHandler<-T> {
public function handle(T $item): void;
}
class AnimalHandler implements IHandler
public function handle(Animal $a): void { / 全ての動物を処理 / }
}
function execute(IHandler
$handler.handle(new Dog());
}
// 利用例
$animalHandler = new AnimalHandler();
execute($animalHandler); // 反変により、AnimalHandler は IHandler
知見: `-T` を付与した型パラメータは、引数(入力)にしか使えない。戻り値として生成することはできない。これは、APIのイベントリスナーやコールバック関数を設計する際に、コードの再利用性を劇的に向上させる。
—
4. プロダクションコードにおける設計の鉄則
実務において、複雑なジェネリクスを多用しすぎると、型チェッカーの推論コストが増大し、HHVMのビルド時間を圧迫する。以下の原則を守れ。
1. デフォルトは不変(Invariance)でよしとせよ: 明示的な `+` や `-` が不要なら、付けないのが最も堅牢だ。
2. インターフェースの分離: `IReadOnlyCollection` と `IWriteOnlyCollection` を分け、それぞれ `+T` と `-T` を適切に配置することで、複雑な継承関係を解決できる。
3. パフォーマンスへの意識: `+T` / `-T` は単なるコンパイル時のチェックであり、実行時のHHVMのオーバーヘッドはゼロだ。型安全性を高めるための「コストのかからない最適化」である。
最後に
Hackの型システムは、あなたのコードを縛る鎖ではない。それは、実行時に発生しうる「ありとあらゆるバグ」を、コードを書いているその瞬間に叩き潰すための最強の防壁だ。
共変・反変を理解することは、Hackの深淵に触れることと同義である。もしコードレビューで「なぜここで `+T` を使わないのか?」と問えるようになれば、あなたは既にプロジェクトのアーキテクトとして一段上の視座に立っている。
さあ、型システムを使い倒し、堅牢なプロダクトを設計せよ。