HaskellのFunctor・Applicative・Monad

文脈を持つ値へ関数を適用し、独立した計算や依存する計算を組み合わせる抽象化を比較する。

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

キーワード: fmap / applicative / bind / monad / context

概要

Functor、Applicative、Monadは、MaybeEither、リスト、IOなど、追加の文脈を持つ値を組み合わせるための抽象化です。

モナドを特殊な容器として暗記するより、通常の関数を「失敗するかもしれない値」や「IOを伴う値」にどう適用するか、という問題から見ると理解しやすくなります。

前提となる例

通常の関数があるとします。

addTax :: Int -> Int
addTax price = price * 110 `div` 100

普通のIntにはそのまま適用できます。

addTax 1000

一方、Maybe Intは値がない可能性を含むため、通常の関数適用だけでは扱えません。

Functor:文脈を保ったまま変換する

fmapは、文脈の中の値に関数を適用します。

fmap addTax (Just 1000) -- Just 1100
fmap addTax Nothing     -- Nothing

型の形は次のとおりです。

fmap :: Functor f => (a -> b) -> f a -> f b

Maybeという文脈はそのままに、中のaだけをbへ変換します。

Applicative:独立した文脈を組み合わせる

2つのMaybeから値を作る場合は、Applicativeを使えます。

data User = User String Int

makeUser :: Maybe String -> Maybe Int -> Maybe User
makeUser name age = User <$> name <*> age

名前か年齢のどちらかがNothingなら、結果もNothingです。

各入力は互いの結果に依存せず、最初から必要な計算の形が決まっています。フォームの複数項目を検証して結果を組み立てるような場面と相性があります。

Monad:前の結果で次の処理を決める

次の処理が前の結果に依存する場合は、Monadの連結が必要になります。

parseAge :: String -> Maybe Int
parseAge text =
  case reads text of
    [(age, "")] -> Just age
    _           -> Nothing

validateAge :: Int -> Maybe Int
validateAge age
  | age >= 18 = Just age
  | otherwise = Nothing

adultAge :: String -> Maybe Int
adultAge text = do
  age <- parseAge text
  validateAge age

parseAgeが失敗すれば後続処理は行われません。成功した場合だけ、その結果ageを使って次の計算を選びます。

中心となる演算>>=の形は次のとおりです。

(>>=) :: Monad m => m a -> (a -> m b) -> m b

文脈を持つ値m aと、その中身を受け取って次の文脈m bを返す関数をつなぎます。

3つの違い

抽象化できること次の処理
Functor文脈内の値を変換する元の値には依存しない変換
Applicative複数の独立した文脈を組み合わせる全体の形を先に決められる
Monad文脈を持つ処理を順番につなぐ前の結果で次を選べる

MonadはApplicativeより常に優れた書き方という意味ではありません。依存関係がない処理をApplicativeで表すと、処理構造が型とコードに現れます。

IOも同じ仕組みでつなぐ

do記法はIO専用ではありません。MaybeEitherなど、Monadの処理を順番につなぐ構文です。

main :: IO ()
main = do
  name <- getLine
  putStrLn ("こんにちは、" ++ name)

前のIOで得た値を次のIOで使うため、Monadとして連結されています。IOそのものを「モナドの正体」と考えるより、IOがMonadの実例の一つだと捉えます。

理解するときの注意

参考資料

更新履歴

  • 初版作成

関連項目

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