代数的データ型

直和型と直積型を組み合わせてデータを表現する型システムの概念

型システム関数型プログラミング

代数的データ型とは

代数的データ型 (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} />;
  }
}

なぜ重要か

代数的データ型を使うと、「ローディング中なのにデータがある」のような不正な状態を型レベルで排除できる。パターンマッチで全ケースを処理しないとコンパイルエラーになるため、ケースの漏れを防げる。型定義自体がドキュメントとして機能し、コードの意図が明確になる。

代数的データ型の背景や設計思想は関連書籍に詳しい。

関連用語