【入門編】Hackの『Shapes』型における構造的部分型と名前付き型の境界線:設計時に迷わないための使い分け基準 – Hack言語 コア・静的型システムとHHVMのアーキテクチャ解析バイブル

Hack言語の「Shapes」と「名前付き型」:設計の迷いを断ち切る境界線

こんにちは。Hack言語の世界へようこそ。
HHVMの深淵を覗き込み、型チェッカーと毎日対話していると、多くの開発者が「このデータ構造、`shape`で定義すべきか、それとも`class`(名前付き型)にするべきか?」という迷宮に足を踏み入れていることに気づきます。

Hackの`shape`は構造的部分型(Structural Subtyping)の恩恵を最大限に引き出せる強力な武器ですが、闇雲に使うとドメインモデルが崩壊します。今日は、この境界線をどう引くべきか、現場の視点から紐解いていきましょう。

—

1. そもそも「Shapes」とは何者か?

`shape`は、一言で言えば「キーと値の型が固定された、軽量な連想配列」です。

type UserProfile = shape(
‘id’ => int,
‘username’ => string,
‘is_verified’ => bool,
);

この定義により、`$user[‘id’]` は常に `int` であることが保証されます。クラスを定義するほどではないが、ただの `dict` では不安だ…という時に、型チェッカーがその整合性を厳格に守ってくれる。これが`shape`の真骨頂です。

—

2. 構造的部分型 vs 名前付き型:境界線はここだ

では、なぜ迷うのでしょうか?それは、`shape`が「構造」だけで型を判断するからです。

構造的部分型(Shape)の強み:柔軟性

`shape`は「同じ形をしていれば、それは同じ型」とみなされます。APIのレスポンスや、一時的なデータ加工には最適です。

名前付き型(Class)の強み:ドメインの固定

`class`は「同じ形をしていても、別物」として扱われます。そこにメソッド(振る舞い)を付与し、状態の整合性をカプセル化できるのはクラスだけです。

使い分けの黄金ルール

  • Shape: 「ただのデータ」を運ぶとき。外部との境界(JSONデコード、設定値など)。
  • Class: 「振る舞い」を伴うとき。ビジネスロジックの中心、状態変化が伴うオブジェクト。

—

3. 実践:設計の現場で見極める

例えば、「ユーザーの住所情報」を扱うケースを考えてみましょう。

Shapeで書くべきケース(情報の受け渡し)

APIから受け取ったJSONをバリデーションするような場所では、`shape`が最適です。

// 外部からのデータ構造を定義
type Address = shape(
‘city’ => string,
‘zip’ => string,
);

function processAddress(Address $addr): void {
// 構造さえ合っていればOK。軽量で高速。
echo “City: ” . $addr[‘city’];
}

Classで書くべきケース(ロジックの付与)

もし住所に「配送可能エリアかどうかを判定する」というビジネスルールが含まれるなら、`class`に昇格させるべきです。

class Address {
public function __construct(
private string $city,
private string $zip,
) {}

// ここにビジネスロジックを持たせる
public function isDeliverable(): bool {
return $this->city === ‘Tokyo’;
}
}

—

4. 陥りやすい罠:エラーを読み解く

初学者がよく直面するのが「形は同じなのに型エラーになる」という現象です。

type A = shape(‘id’ => int);
type B = shape(‘id’ => int);

function test(A $a): void { / … / }

$b = shape(‘id’ => 1);
test($b); // これ、実は通ります!

Hackの`shape`は構造が一致していれば代入可能ですが、「名前付きのエイリアス」を定義した瞬間、意図が明確になります。

陥りやすいミス:
`shape`に新しいフィールドを追加した際、古い関数が「未定義のキーへのアクセス」でエラーを吐くのは、実は型チェッカーがあなたを守っている証拠です。これを避けるために、`optional`なフィールド(`?`を付ける)を適切に使い分けましょう。

type OptionalShape = shape(
‘id’ => int,
‘email’ => ?string, // emailはあってもなくても良い
);

—

最後に:型と友だちになるために

Hackの型システムは、あなたの「意図」をコンパイラに伝えるための言語です。

  • 「ただのデータ」なら`shape`で軽快に。
  • 「システムの状態」なら`class`で堅牢に。

この使い分けができるようになれば、あなたのコードは格段に読みやすく、そして壊れにくくなります。「型エラー」は敵ではありません。あなたの設計の甘さを指摘してくれる、最も有能なレビュアーだと思ってください。

ここをクリアすれば、Hackの基本はバッチリマスターできたも同然です。さあ、次はどんな複雑なドメインに挑んでみますか?またいつでも相談してくださいね。

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