モナド
値をコンテキスト (失敗、非同期、リスト) で包み、連鎖的に処理する関数型プログラミングの抽象
モナドとは
モナド (Monad) は、値をコンテキスト (失敗の可能性、非同期、複数の値) で包み、flatMap (bind) で連鎖的に処理する関数型プログラミングの抽象である。Promise、Option、Result、Array が身近なモナドだ。
日常のモナド: Promise
日常のモナド: Promise のコード例を示す。
// Promise はモナド: 「非同期」というコンテキストで値を包む
const result = await Promise.resolve(1) // Promise<number>
.then(x => Promise.resolve(x + 1)) // flatMap: number → Promise<number>
.then(x => Promise.resolve(x * 2)); // flatMap: number → Promise<number>
// result: 4
then は flatMap に相当する。値を取り出し、新しい Promise を返す関数を適用する。
モナドの 3 つの要素
モナドの 3 つの要素を以下に示す。
| 要素 | 説明 | Promise での例 |
|---|---|---|
| 型コンストラクタ | 値をコンテキストで包む | Promise<T> |
of (unit/return) |
値をモナドに入れる | Promise.resolve(42) |
flatMap (bind/then) |
モナド内の値に関数を適用 | .then(x => ...) |
身近なモナド
身近なモナドを以下にまとめる。
| モナド | コンテキスト | flatMap |
|---|---|---|
Promise<T> |
非同期 | .then() |
Option<T> |
値の有無 | .flatMap() |
Result<T, E> |
成功/失敗 | .flatMap() |
Array<T> |
複数の値 | .flatMap() |
Option モナドの連鎖
Option モナドの連鎖のコード例を示す。
type Option<T> = { kind: 'some'; value: T } | { kind: 'none' };
function flatMap<T, U>(opt: Option<T>, fn: (v: T) => Option<U>): Option<U> {
return opt.kind === 'some' ? fn(opt.value) : { kind: 'none' };
}
// null チェックの連鎖をモナドで解決
const result = flatMap(findUser('123'), user =>
flatMap(findOrder(user.orderId), order =>
findProduct(order.productId)
)
);
// どこかで none が返ったら、以降の処理はスキップされる
Rust の ? 演算子
Rust の ? 演算子のコード例を示す。
// Rust の ? はモナドの flatMap の糖衣構文
fn get_product_name(user_id: &str) -> Result<String, Error> {
let user = find_user(user_id)?; // Err なら早期リターン
let order = find_order(&user.order_id)?;
let product = find_product(&order.product_id)?;
Ok(product.name)
}
なぜモナドが難しいと言われるか
モナド自体は「flatMap を持つコンテナ」というシンプルな概念だが、圏論 (Category Theory) の用語で説明されることが多く、不必要に難解に見える。Promise を使ったことがあれば、既にモナドを使っている。
TypeScript での実用
TypeScript では明示的にモナドを実装する必要はほとんどない。Promise、Array.flatMap、neverthrow の Result 型が実質的にモナドとして機能する。
理論と実装の両面から学ぶなら関連書籍が参考になる。
この記事は役に立ちましたか?
関連用語
Option / Result 型
null や例外の代わりに、値の有無やエラーを型で表現する関数型プログラミングのパターン
純粋関数
同じ入力に対して常に同じ出力を返し、副作用を持たない関数
高階関数
関数を引数に取る、または関数を返す関数で、関数型プログラミングの基本概念
境界づけられたコンテキスト
ドメインモデルが一貫した意味を持つ範囲を明確に区切り、モデルの曖昧さを排除する DDD の戦略的パターン
LCP
ビューポート内の最大コンテンツ要素が表示されるまでの時間を測定する Core Web Vitals の指標
関数型プログラミング
副作用を避け、純粋関数と不変データを中心にプログラムを構築するパラダイム
関連する記事
OS・低レイヤー本ガイド - コンピュータの仕組みを学ぶ技術書の選び方
OS、コンパイラ、ネットワークなど低レイヤーを学べる技術書の 4 ジャンルと、どこから始めるべきかの指針、賞味期限の見極め方を紹介します。
「動くコード」と「良いコード」の間にある本
コードが動くようになった後、次に何を学べばよいのか。「動くコード」を「良いコード」に変えるために必要な知識と、それを効率的に学べる本の選び方を解説します。
コードを「書く力」と「読む力」は別物 - 読解力を鍛える技術書の使い方
プログラミングの「書く力」ばかり鍛えていませんか。他人のコードを正確に読み解く力は、技術書を使って意識的に鍛えられます。読解力を高める具体的な方法を紹介。