パターンマッチ
値の構造に基づいて分岐処理を行う制御構文で、switch 文の強化版
プログラミング型システム
パターンマッチとは
パターンマッチは、値の構造に基づいて分岐処理を行う制御構文である。単純な値の比較だけでなく、型の判別、構造の分解、条件の組み合わせを 1 つの構文で表現できる。Rust の match、Haskell の case が代表例。
Rust の match
enum Shape {
Circle(f64),
Rectangle(f64, f64),
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle(w, h) => w * h,
}
// 全パターンを網羅しないとコンパイルエラー
}
TypeScript の Discriminated Union
type Result<T> =
| { ok: true; value: T }
| { ok: false; error: string };
function handle(result: Result<number>) {
if (result.ok) {
console.log(result.value); // TypeScript が value の存在を保証
} else {
console.error(result.error); // TypeScript が error の存在を保証
}
}
網羅性チェック (Exhaustiveness Check)
type Color = 'red' | 'green' | 'blue';
function toHex(color: Color): string {
switch (color) {
case 'red': return '#ff0000';
case 'green': return '#00ff00';
case 'blue': return '#0000ff';
// 'blue' を忘れると TypeScript がエラーを出す
}
}
// never 型で網羅性を強制
function assertNever(x: never): never {
throw new Error(`Unexpected: ${x}`);
}
Rust の高度なパターン
match value {
0 => println!("zero"),
1..=9 => println!("single digit"), // 範囲パターン
n if n % 2 == 0 => println!("even"), // ガード条件
_ => println!("other"), // ワイルドカード
}
// 構造体の分解
match point {
Point { x: 0, y } => println!("on y-axis at {y}"),
Point { x, y: 0 } => println!("on x-axis at {x}"),
Point { x, y } => println!("({x}, {y})"),
}
if-else チェーンとの比較
| 観点 | if-else | パターンマッチ |
|---|---|---|
| 網羅性チェック | なし | コンパイラが保証 |
| 構造の分解 | 手動 | 自動 |
| 可読性 | 条件が増えると低下 | 構造的で読みやすい |
Lambda ハンドラでの活用
type Event =
| { source: 'api'; httpMethod: string; path: string }
| { source: 'sqs'; Records: SQSRecord[] }
| { source: 'schedule' };
function route(event: Event) {
switch (event.source) {
case 'api': return handleApi(event);
case 'sqs': return handleSqs(event);
case 'schedule': return handleSchedule();
}
}
全体像を把握するには関連書籍も有用。