こんにちは!Hack言語の世界へようこそ。
普段、他の動的言語や、ちょっと緩めの型システムを持つ言語から入った方にとって、外部APIから返ってくるJSONデータをどう扱うかは最初の大きな壁になりがちですよね。「とりあえず連想配列(array)に入れてキーアクセスすればいいか」と油断していると、本番環境で突然 `Undefined index` のエラーを踏んで頭を抱えることになります。
でも、安心してください。Hack言語には、この「動的な連想配列地獄」をエレガントに、かつ鉄壁の安全性で解決する「Shape(シェイプ)型」という最強の武器が用意されています。
今回は、初心者の方でも迷わずShape型を使いこなせるように、その本質と実践的なテクニックを優しく丁寧に解説していきますね。ここをクリアすれば、Hackの厳格な型システムの心地よさが一気に体感できますよ!
—
1. なぜ「普通の連想配列」では危険なのか?
外部のAPIを叩いたとき、大抵は以下のようなJSONレスポンスが返ってきますよね。
{
“id”: 42,
“name”: “Alice”,
“email”: “alice@example.com”
}
これをPHPや他の言語のノリで、単なる連想配列 `array
<<__Strict>>
namespace Hack\Guide;
// 危険な例:普通の連想配列をそのまま扱う
function handle_response(array
// もしAPIの仕様変更で “name” が “user_name” に変わったら?
// もし “id” が文字列で返ってきたら?
// 型チェッカーは何も守ってくれません。
echo “User: ” . $response[‘name’];
}
このコードの何が問題か分かりますか?
「どのキーが存在し、それぞれの値が何型なのか」を、型チェッカーが一切把握できない点です。コードを書いた本人すら、数日後には「あれ、この配列のキー名って何だっけ?」とAPIドキュメントを見直す羽目になります。
ここで登場するのが、構造化された配列の型定義である Shape型 です。
—
2. Shape型(形状型)とは何か?
Shape型とは、一言で言えば「キーの名前と値の型が完全に固定された、軽量な構造体」です。
イメージとしては、クラス(Class)を作るほど大げさではないけれど、連想配列よりも厳格に構造を定義したいときに使います。
先ほどのユーザー情報を表すJSONを、Shape型を使って定義してみましょう。
<<__Strict>>
namespace Hack\Guide;
// 1. UserDataというShape型を定義する
type UserData = shape(
‘id’ => int,
‘name’ => string,
‘email’ => string,
);
function handle_response(UserData $response): void {
// 型安全にアクセスできる!
echo “Hello, ” . $response[‘name’] . “!\n”;
// もし存在しないキーにアクセスしようとすると…?
// echo $response[‘age’]; // <- 型チェッカーが即座にコンパイルエラーを出してくれます!
}
Shape型のスゴいところ(イメージ図)
通常の配列が「中身が何が入っているか開けてみるまで分からないおもちゃ箱」だとすると、Shape型は「綺麗に仕切られた引き出し」です。
[普通の配列 (array
📦 箱を開けるまで中身が分からない(エラーは実行時まで隠れる)
[Shape型 (shape(…))]
🗄️ [id: int] [name: string] [email: string]
↑ 型チェッカーが厳格にスキャンし、存在しないキーや型違いを完全ガード!
型チェッカーが「この配列には絶対に `id` (int), `name` (string), `email` (string) がこの通りに入っている」と保証してくれるため、安心してビジネスロジックに集中できるわけです。
—
3. 実践:APIレスポンスを安全にマッピングする
それでは、実際に外部APIから受け取ったデータを、Shape型を使って安全に処理するコードを見てみましょう。
<<__Strict>>
namespace Hack\Guide;
// ユーザーデータのShape型定義
type UserResponse = shape(
‘id’ => int,
‘name’ => string,
‘email’ => string,
);
// APIからJSON文字列が返ってきたと仮定
function fetch_external_api_mock(): string {
// 実際には curl や HHVMのHTTPクライアントなどで取得するJSON
return ‘{“id”: 101, “name”: “Bob Smith”, “email”: “bob@example.com”}’;
}
function main(): void {
$json_string = fetch_external_api_mock();
// JSONをデコード(この時点では dynamic型 や array型 になることが多い)
$raw_data = json_decode($json_string, true);
// 【重要】ここで外部の動的データを、厳格なShape型へキャスト(安全なマッピング)する
// ※実際の現場では、ここでバリデーションライブラリ等を通すか、型アサーションを行います
if (!is_array($raw_data)) {
throw new \Exception(“Invalid JSON response”);
}
// Shape型へのマッピング(Hackの型チェッカーを納得させる安全な変換)
$user: UserResponse = shape(
‘id’ => (int)$raw_data[‘id’],
‘name’ => (string)$raw_data[‘name’],
‘email’ => (string)$raw_data[‘email’],
);
// 完全な型安全の恩恵を受ける処理
process_user($user);
}
function process_user(UserResponse $user): void {
// $user[‘name’] が string であることが保証されているため安心
echo “Processing user: ” . $user[‘name’] . ” (” . $user[‘email’] . “)\n”;
}
—
4. 初心者がハマりやすい「罠」と文法エラー
Shape型を使い始めるときに、多くの開発者が一度は踏んでしまうポイントをいくつかご紹介しておきますね。これを知っておけば怖くありません!
罠①:オプショナル(省略可能)なキーの書き方を間違える
APIによっては、データが存在したりしなかったりするキー(例: `bio` や `twitter_id`)がありますよね。そんなときは、`?` をつけてオプショナルShapeにします。
type UserWithBio = shape(
‘id’ => int,
‘name’ => string,
?’bio’ => ?string, // キー自体がない場合がある、または値がnullの可能性がある
);
注意: オプショナルキーにアクセスする場合、キーが存在するかどうかを `Shapes::keyExists($user, ‘bio’)` などで事前にチェックするか、適切にハンドリングする必要があります。
罠②:配列の代入でキーの過不足エラーが出る
Shape型は「定義された通りの構造」を厳格に求めます。定義されていないキーを勝手に追加したり、逆に必須のキーを忘れたりすると、HHVMの型チェッカーが容赦なく赤線を引きます。
// エラー例:定義にない ‘age’ を入れようとした場合
$user = shape(
‘id’ => 1,
‘name’ => ‘Alice’,
‘email’ => ‘alice@example.com’,
‘age’ => 25, // 🛑 Type checker error: 予期しないキーです!
);
—
まとめ
いかがでしたでしょうか?
Hack言語のShape型は、動的な配列の利便性を残しつつ、オブジェクト指向の堅牢性をいいとこ取りした素晴らしい機能です。
- 普通の配列のままでは、API仕様変更やタイポの温床になる
- Shape型を使えば、キー名と値の型をコンパイル時に完全に保証できる
- オプショナルなフィールドもうまく表現できる
ここをマスターすれば、あなたの書くHackコードの安全性と美しさは劇的に跳ね上がります。「動的言語の書きやすさ」と「静的言語の安心感」の最高のマリアージュを、ぜひ日々の開発で楽しんでくださいね!