こんにちは。Hackの深淵へようこそ。
Hackの静的型システムは、単なる「エラーを防ぐためのガードレール」ではありません。あれは、実行時のメモリ安全性と、複雑なロジックをスケールさせるための「数学的な証明」そのものです。
今日は、多くのエンジニアが直感に反して苦しむ「ジェネリクスの変位(Variance:共変・反変)」という難所に挑みましょう。ここを理解すれば、あなたはただHackを書く人から、Hackを「設計する人」へと進化できますよ。
—
なぜ「List」に「List」を代入できないのか?
まず、直感的な疑問から始めましょう。
「犬(Dog)は動物(Animal)の一種なのだから、`List
しかし、Hackの型チェッカーは容赦なく冷たくエラーを返します。なぜでしょう?
共変(Covariance)の罠:読み込みだけなら安全だが…
もし `List
// 概念的な例
function feedAnimals(Vector
// ここでCat(猫)を追加できてしまう!
$animals->add(new Cat());
}
$dogs = Vector
feedAnimals($dogs); // もしこれが許されると…
// $dogsの中身が、DogとCatが混在する「汚染されたリスト」になる!
これが「共変」の危険性です。「読み込み専用」であれば安全ですが、「書き込み(追加)」ができる以上、型安全は崩壊するのです。だからこそ、Hackではデフォルトでジェネリクスは「不変(Invariant)」として扱われます。
—
Hackの「型システムを掌握する」ためのキーワード
ここを整理すれば、もう迷うことはありません。
- 不変 (Invariant): `Vector
` はこれ。`Dog` と `Animal` は別物として扱う。最も安全。 - 共変 (Covariance):`+T`。読み取り専用。型が広がる方向への代入が可能。
- 反変 (Contravariance):`-T`。書き込み専用。型が狭まる方向への代入が可能。
1. 共変 (`+T`): データを「取り出す」とき
「動物を扱う箱」から、中身を取り出すだけなら、それは「動物」として扱っても問題ありませんよね。`KeyedIterable` など、イテレータ系インターフェースでよく見かけます。
// +T を使うと、サブタイプをより広い型に代入できる
interface ReadableBox<+T> {
public function get(): T;
}
function process(ReadableBox
$animal = $box->get(); // 何が入っていてもAnimalとして扱えるので安全
}
// ReadableBox
2. 反変 (`-T`): データを「消費する」とき
逆に、データを「受け取る」だけのインターフェースなら、逆の代入が可能になります。「動物ならなんでも扱える機械」に「犬専用の機械」を渡すのは、論理的に正しいですよね。
// -T を使うと、スーパタイプをより狭い型に代入できる
interface Consumer<-T> {
public function consume(T $item): void;
}
function run(Consumer
$dogConsumer->consume(new Dog());
}
// Consumer
$animalConsumer = new AnimalConsumer();
run($animalConsumer);
—
現場でエラーに直面したときの処方箋
開発中に `Expected Vector
1. 「そのジェネリクス、書き込みが必要ですか?」
- もし読み取り専用なら、`Vector` ではなく `KeyedIterable` のような、共変(`+T`)が定義されたインターフェースへ型を絞り込みましょう。
2. 「型の階層が深すぎませんか?」
- クラスの継承関係が複雑すぎると、型チェッカーが追いきれず、安全のために厳格な「不変」を要求します。インターフェースを小さく分割(インターフェース分離の原則)することで解決するケースがほとんどです。
3. 「無駄にジェネリクスを使いすぎていませんか?」
- 無理にジェネリクスで汎用化せず、型パラメータを具体的に固定することで、Hackの静的解析の恩恵を最大限に受けられます。
—
まとめ:型は「制約」ではなく「設計図」
Hackの静的型システムが厳しいのは、あなたのコードが「将来にわたって壊れないこと」を数学的に保証するためです。
- 取り出すだけなら「共変 (`+T`)」
- 受け取るだけなら「反変 (`-T`)」
- 読み書きするなら「不変(ジェネリクスそのままで)」
この三原則を意識するだけで、あなたの書くコードは驚くほど堅牢になります。型エラーは「邪魔な警告」ではありません。設計上の矛盾を教えてくれる「頼もしいパートナー」なのです。
さあ、この理論を武器に、今日も最高に美しいHackコードを書き上げてください。また何かあれば、いつでも聞きに来てくださいね。応援しています!