Haskellの代数的データ型とパターンマッチ

直積型と直和型を使って業務上の状態を表し、パターンマッチで場合ごとの処理を記述する。

作成日: 2026-08-03 / 更新日: 2026-08-03

キーワード: algebraic data type / sum type / product type / pattern matching

概要

代数的データ型は、複数の値をまとめたり、取り得る状態を列挙したりするための型です。

Haskellでは、データの形を先に定義し、パターンマッチによって形ごとの処理を記述します。文字列や数値の約束事だけで状態を表すより、不正な状態を型で減らせます。

直積型

複数の値をすべて持つデータは、直積型として表せます。

data User = User
  { userId   :: Int
  , userName :: String
  }

Userは、IntのIDとStringの名前の両方を持ちます。Javaの単純なデータクラスに近い形です。

直和型

複数の候補のうち、いずれか一つを取るデータは直和型として表せます。

data PaymentStatus
  = Pending
  | Authorized
  | Captured
  | Cancelled
  | Failed String

PaymentStatusは、定義した状態のいずれかです。Failedだけは理由を表す文字列も持ちます。

単なる文字列で"pending"などを管理する場合と違い、スペルミスや未定義の値を入り込ませにくくなります。

パターンマッチ

データコンストラクタごとの処理を、パターンマッチで書けます。

displayStatus :: PaymentStatus -> String
displayStatus Pending        = "処理待ち"
displayStatus Authorized     = "承認済み"
displayStatus Captured       = "売上確定"
displayStatus Cancelled      = "取消済み"
displayStatus (Failed reason) = "失敗: " ++ reason

新しい状態を追加したのに処理を書き忘れると、コンパイラの警告によって漏れを見つけられます。GHCでは不完全なパターンを警告として扱う設定を有効にしておくことが重要です。

Maybeで値の不在を表す

値が存在しない可能性は、nullの代わりにMaybeで表せます。

findUserName :: Int -> Maybe String
findUserName 1 = Just "ishi"
findUserName _ = Nothing

利用側はJustNothingの両方を扱います。

message :: Maybe String -> String
message (Just name) = "こんにちは、" ++ name
message Nothing     = "ユーザーが見つかりません"

値がない場合を型に含めることで、呼び出し側に処理を促せます。

Eitherで成功と失敗を表す

失敗理由が必要な処理にはEitherを使えます。

authorize :: Int -> Either String PaymentStatus
authorize amount
  | amount <= 0 = Left "金額が不正です"
  | otherwise   = Right Authorized

慣例として、Leftに失敗、Rightに成功を入れます。例外を投げる前に、想定内の失敗を戻り値の型として表現できないか検討できます。

不正な状態を表現しにくくする

次のようにBoolを複数並べると、矛盾した組み合わせを作れます。

data Payment = Payment
  { isAuthorized :: Bool
  , isCaptured   :: Bool
  , isCancelled  :: Bool
  }

代わりに状態ごとの型を定義すると、「売上確定済みで未承認」のような組み合わせを減らせます。

ただし、型だけで業務ルールのすべてを保証できるわけではありません。状態遷移、永続化、外部システムとの整合性は別に設計する必要があります。

設計で使う観点

Haskellの代数的データ型は、関数型言語固有の小技というより、ドメインの状態をコードへ正確に写すための道具として見ると理解しやすくなります。

参考資料

更新履歴

  • 初版作成

関連項目

このページを参照しているページ