モナド もなど
関数型プログラミングHaskell副作用型クラスバインド演算子コンテナ
モナドについて教えて
簡単に言うとこんな感じ!
モナドは「値を包んだ箱」だよ! 箱の中身を取り出して処理して、また箱に戻す——その一連の操作をルール通りに安全につなげられる仕組みなんだ。副作用やエラー処理をスッキリ書けるようにする「お作法」ってこと!
モナドとは
モナドは、関数型プログラミングにおいて「ある値を文脈(コンテキスト)ごと包んで、安全に処理をつなげていく」ための設計パターン・抽象概念です。数学の圏論(カテゴリ理論)が起源ですが、プログラミングの文脈では「副作用を扱うための構造」として広く知られています。
具体的には「失敗するかもしれない処理」「ログを伴う処理」「非同期処理」など、純粋な関数だけでは扱いにくい”文脈付きの値”を一貫した方法で操作するための仕組みです。たとえばJavaScriptの Promise や、HaskellのIOモナドなどが代表例です。
「モナドがわかった瞬間、人に説明できなくなる」というジョークがあるほど直感的に掴みにくい概念ですが、本質は「箱に値を入れ、箱のまま処理をチェーンしていくルール」です。一度腑に落ちると、コードの見通しが劇的によくなります。
モナドの3つの要素と法則
モナドは以下の3つの構成要素を持ちます。
| 要素 | 別名 | 役割 |
|---|---|---|
unit(return) | pure / of | 普通の値をモナドの箱に包む |
bind(>>=) | flatMap / then / chain | 箱から値を取り出して関数を適用し、また箱に戻す |
型コンストラクタ M | コンテナ | 値を包む「箱」そのもの(Maybe、IO、List など) |
モナド則(守らないとモナドとは言えない)
モナドとして認められるには、以下の3つの法則を満たす必要があります。
1. 左単位元則: return a >>= f ≡ f a
(箱に入れてすぐ取り出すなら、最初から取り出すのと同じ)
2. 右単位元則: m >>= return ≡ m
(取り出してまた箱に戻すなら、そのまんまと同じ)
3. 結合則: (m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
(処理のつなぎ順は、括弧の付け方によらず結果が同じ)
代表的なモナドの種類
| モナド名 | 扱う文脈 | 言語での例 |
|---|---|---|
Maybe / Option | 失敗するかもしれない処理 | Haskell Maybe、Scala Option |
Either / Result | 成功か失敗かの二択 | Rust Result、F# Result |
IO | 入出力などの副作用 | Haskell IO |
List | 複数の可能性を同時に扱う | Haskell [] |
Promise / Future | 非同期処理 | JavaScript Promise |
State | 状態を引き回す処理 | Haskell State |
Writer | ログを伴う処理 | Haskell Writer |
歴史と背景
- 1940〜1960年代 — 数学者サンダース・マクレーンとサミュエル・アイレンベルグが圏論(Category Theory)を確立。モナドの数学的概念はその一部として定式化された
- 1989年 — コンピュータ科学者フィリップ・ワドラー(Philip Wadler)が論文「Comprehending Monads」でモナドをプログラミングに応用する方法を提案
- 1990年代 — Haskell がモナドをIOや状態管理の中核に据えて採用。純粋関数型言語で副作用を扱う解として定着
- 2000年代 — ScalaのOption/Future、F#のコンピュテーション式など、Haskell以外の言語にも広まる
- 2012年前後 — JavaScriptの
Promiseが普及。「モナド的」な概念がWeb開発者にも身近になる - 2015年以降 — Rust の
Option/Result、Swift のOptionalなど、現代的な言語が軒並みモナド的な構造を標準採用。モナドは「難解な理論」から「実用的な道具」へ
Maybeモナドで見る具体的なイメージ
「値がないかもしれない」ケースを安全につなぐMaybeモナドは、モナドの入門として最もわかりやすい例です。
モナドをコードで見る(Haskell vs JavaScript比較)
-- Haskell: Maybeモナドでチェーン
findUser :: Int -> Maybe User
getName :: User -> Maybe String
getEmail :: String -> Maybe Email
result :: Maybe Email
result = findUser 1 >>= getName >>= getEmail
-- どこかでNothingが返ったら、その先は自動でスキップ!
// JavaScript: Promiseも「モナド的」
fetchUser(1)
.then(getName) // thenがbind(>>=)に相当
.then(getEmail) // チェーンでつながる
.catch(handleError); // Nothingに相当するエラーを最後に1箇所で処理
// Scala: for式(ドゥ記法に相当)でモナドをスッキリ書く
val result = for {
user <- findUser(1) // Option[User]
name <- getName(user) // Option[String]
email <- getEmail(name) // Option[Email]
} yield email
モナドと「ファンクタ」「アプリカティブ」の関係
包む(map) 関数も包む(ap) 連鎖できる(bind)
通常の値 ──────→ Functor ──────────→ Applicative ──────────→ Monad
↑ ↑ ↑
fmap <*> >>=
「箱の中身を 「箱に入った関数を 「箱から取り出して
変換できる」 箱の値に適用」 別の箱を返す関数へ」
モナドはFunctorとApplicativeの上位概念であり、「bind(>>= )ができる」点が最大の特徴です。
関連する規格・RFC
※ モナドは数学・プログラミングの抽象概念であり、IETF・ISO・IEEE等の標準規格は存在しないため、このセクションは省略します。
関連用語
- 関数型プログラミング — 副作用を排し、関数の組み合わせでプログラムを構築するパラダイム
- 純粋関数 — 同じ入力には必ず同じ出力を返し、副作用を持たない関数
- ファンクタ — 値を包んだコンテナに
mapを適用できる構造。モナドの土台 - 型クラス — Haskellなどで型に対してインターフェースのような制約を与える仕組み
- Maybe型 — 値があるかもしれない・ないかもしれない、を型で表現するモナドの代表例
- Promise — JavaScriptの非同期処理の仕組み。モナド的なチェーン構造を持つ
- 副作用 — 関数の外部状態を変更する操作。IOやStateモナドが扱う対象
- 圏論 — モナドの数学的起源。対象・射・合成を扱う抽象数学の分野