代数的データ型
直和型と直積型を組み合わせてデータを表現する型システムの概念
型システム関数型プログラミング
代数的データ型とは
代数的データ型 (Algebraic Data Type, ADT) は、直和型 (Sum Type) と直積型 (Product Type) を組み合わせてデータを表現する型システムの概念である。Rust の enum、TypeScript の Discriminated Union が直和型に相当する。
直積型 (Product Type)
「A かつ B」。全てのフィールドを持つ。
// TypeScript: オブジェクト型 = 直積型
type User = { name: string; age: number };
// name と age の両方を持つ
// 取りうる値の数 = string の数 × number の数
直和型 (Sum Type)
「A または B」。いずれか 1 つの形態を取る。
// TypeScript: Discriminated Union = 直和型
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'rectangle'; width: number; height: number }
| { kind: 'triangle'; base: number; height: number };
// circle, rectangle, triangle のいずれか 1 つ
パターンマッチ
function area(shape: Shape): number {
switch (shape.kind) {
case 'circle': return Math.PI * shape.radius ** 2;
case 'rectangle': return shape.width * shape.height;
case 'triangle': return shape.base * shape.height / 2;
}
}
// TypeScript が網羅性をチェック (全ケースを処理しないとエラー)
Rust の enum (ADT)
enum Shape {
Circle(f64),
Rectangle(f64, f64),
Triangle { base: f64, height: f64 },
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle(w, h) => w * h,
Shape::Triangle { base, height } => base * height / 2.0,
}
}
実用例: API レスポンス
type ApiResult<T> =
| { status: 'success'; data: T }
| { status: 'error'; message: string; code: number }
| { status: 'loading' };
function renderResult(result: ApiResult<User>) {
switch (result.status) {
case 'loading': return <Spinner />;
case 'error': return <Error message={result.message} />;
case 'success': return <UserCard user={result.data} />;
}
}
なぜ重要か
代数的データ型を使うと、「ローディング中なのにデータがある」のような不正な状態を型レベルで排除できる。パターンマッチで全ケースを処理しないとコンパイルエラーになるため、ケースの漏れを防げる。型定義自体がドキュメントとして機能し、コードの意図が明確になる。
代数的データ型の背景や設計思想は関連書籍に詳しい。