関数型プログラミング

モナド もなど

関数型プログラミングHaskell副作用型クラスバインド演算子コンテナ
モナドについて教えて

簡単に言うとこんな感じ!

モナドは「値を包んだ箱」だよ! 箱の中身を取り出して処理して、また箱に戻す——その一連の操作をルール通りに安全につなげられる仕組みなんだ。副作用やエラー処理をスッキリ書けるようにする「お作法」ってこと!


モナドとは

モナドは、関数型プログラミングにおいて「ある値を文脈(コンテキスト)ごと包んで、安全に処理をつなげていく」ための設計パターン・抽象概念です。数学の圏論(カテゴリ理論)が起源ですが、プログラミングの文脈では「副作用を扱うための構造」として広く知られています。

具体的には「失敗するかもしれない処理」「ログを伴う処理」「非同期処理」など、純粋な関数だけでは扱いにくい”文脈付きの値”を一貫した方法で操作するための仕組みです。たとえばJavaScriptPromise や、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 MaybeScala 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 / ResultSwiftOptional など、現代的な言語が軒並みモナド的な構造を標準採用。モナドは「難解な理論」から「実用的な道具」へ

Maybeモナドで見る具体的なイメージ

「値がないかもしれない」ケースを安全につなぐMaybeモナドは、モナドの入門として最もわかりやすい例です。

Maybeモナドのフロー(ユーザーID→名前→メール取得) 成功ルート(Just) findUser(1) Just(User) >>= getName(user) Just("Alice") >>= getEmail(name) Just("a@ex.com") Just "a@ex.com" 失敗ルート(Nothing) findUser(999) Nothing >>= スキップ (呼ばれない) >>= スキップ (呼ばれない) Nothing (伝播) ポイント:一度Nothingになったら、以降の処理は全部スキップされる nullチェックをいちいち書かなくてよい → コードがスッキリ!

モナドをコードで見る(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モナドが扱う対象
  • 圏論 — モナドの数学的起源。対象・射・合成を扱う抽象数学の分野