0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Haskellプログラマは圏論をどう参照してコードを書くのか ── Functor・Monad・自然変換・随伴の思考地図

0
Last updated at Posted at 2026-05-23

thumbnail.jpg

圏論は、Haskellのコードを書くとき、頭の中で本当はどう使われているのか ── 関数合成からモナド・アプリカティブ・モノイド・トラバーサブル・自然変換・随伴まで、実務の使いどころ全地図

本記事の位置づけ

関数型プログラマは、Haskellなどの関数型言語のコードをタイピングしている、まさにその瞬間に、数学の圏論を意識のなかで参照しています。

この一篇の小品記事は、その参照のしかたを、敢えて言語化して抽出し、文章として可視化しようと試みたものです。

本記事が扱う圏論の概念は、大きく8つの柱に分かれます。

すなわち ── Functor(ファンクタ/関手)、Applicative(アプリカティブ)、Monad(モナド)、Semigroup と Monoid(半群とモノイド)、Foldable(畳み込み可能)、Traversable(走査可能)、自然変換(Natural Transformation)、そして随伴(Adjunction) ── の8つです。

concepts.jpg

これら8つの圏論の数学概念を、Haskellなどの関数型プログラミング言語をコード実装していく過程で、意識体験のなかで、どのように参照していると考えられるのか。

いわば、圏論という数学的な構造が、関数型プログラマの頭脳と意識のなかで、思考の枠組みとして、いかに意識体験として立ち現れ、利用されているのか。

その思考の現場を、敢えて言語化したものです。
(Semigroup と Monoid は地続きの一組として、ひとつの柱にまとめています)

この記事とは別の入門者向け記事として、内容を関数合成・ファンクタ・モナドの3つに絞った記事を、別途ご用意させていただく予定です(関数型も圏論も学んだことのないPythonプログラマに向けたものです)。

本記事は、上記の3つの圏論概念に内容を絞り込むことなく、熟練の関数型プログラマが日々参照している、上記の8つの圏論概念で織りなされる圏論的構造のほぼ全域を取り上げます。

本記事は、次のような方に向けています。
 

  • HaskellやScala、あるいはRust・F#・OCamlで、関数型のスタイルをある程度書いてきた方
     
  • FunctorとMonadは使えるものの、Applicative・Traversable・自然変換・随伴となると、「なんとなく使ってはいるが、圏論的に何をしているのかは曖昧だ」と感じている方
     
  • 型クラスの階層(Functor → Applicative → Monad)を、設計の道具として頭に入れたい方
     
  • 「圏論を学ぶと、何が嬉しいのか」を、コードのレベルで腑に落としたい方
     

この記事を読む価値

本記事がお伝えしたいのは、数学の理解ではありません。コードを書くときの思考そのものを、言葉にすることです。

Qiitaで、すでに多くの方が執筆・公開なさっている圏論を題材に取り上げた優れた記事の多くは、「圏論とは何か」をわかりやすく解説してくださっています。

この記事が試みるのは、その先です。
圏論を使って、どう判断し、どう書くか ── 思考のプロセスそのものを、描き出します。

言い換えれば、本記事が描こうとするのは、次の問いです。

Haskellのコードをタイピングして、コードを記述していくまさにその過程で、Haskellプログラマの頭のなかで、圏論という思考のフレームワークがどう、プログラマの意識のなかで参照されているのか。

いわば、関数型プログラミングが実践に移される最中に、圏論がプログラマの意識のなかで、どう参照されていると想像されるのか?本稿は、この点を言語化し、可視化しようと試みたものです。

これが、本記事のいう「思考の地図」です。

圏論的な思考が、関数型プログラミング言語のコード実装の最中に、プログラマの意識の中にどのようなかたちをもった似姿で現れているのか。

型を前にして、頭の中でどう道筋を立てるか。その地図を、Haskellプログラマがどう描けるのか ── その頭の使いかたの一例を、お伝えします。

pic_1.jpg

「一例」と申し上げるのには、理由があります。

これが唯一の正解だと主張するつもりはないからです。

熟練したプログラマの数だけ、頭の中の地図には、それぞれの個性があるはずです。

本記事がお見せするのは、あくまでそのひとつの描きかたにすぎません。それでも、「型を見た瞬間に何を考えるか」を一度言葉にしておくことは、ご自身の地図を育てる、確かな足がかりになるはずです。

なお、本記事はHaskellを題材にしますが、圏論というフレームワークの参照のされかたは、言語によって濃淡が変わるはずです。

たとえばIdrisでは、依存型(値に依存する型)が使えるため、「型がより多くを語る」ぶん、プログラマの意識は圏論的構造に加えて、型そのものが含む証明や制約へも向かうでしょう。

OCamlScalaF# のように、関数型と命令型の双方を併せ持つ言語では、圏論的な参照は局面ごとに濃くなったり薄まったりし、Haskellほど一様には立ち現れないかもしれません。

RustのトレイトやSwiftのプロトコルにも、FunctorやMonadに近い発想は影を落としています。

同じ圏論というレンズでも、言語という器が変われば、その光の差しかたは異なる ── 本記事のHaskellを軸にした記述は、その意味で、ひとつの言語における一例でもあります。

数学的な厳密さは、実務の直感を損なわない範囲で保ちます。証明は最小限にとどめ、「その構造が、コードを書く際の関数型プログラマの意識にどう作用するのか」を、一貫して主眼に据えます。

ここで、念のため、ひとつ申し添えます。

圏論を知らなくても、Haskellのコードを書くことは可能です。

型と抽象化と再利用性を追ううちに、自然と圏論的な語彙へ辿り着く ── 多くの関数型プログラマがたどる道は、むしろそちらです。

最初に圏論を学んでからコードを書くのではなく、コードを書きつづけた果てに、圏論という共通言語へ収束していく。

本記事は、その「辿り着いた先」の風景を、先まわりして一枚の地図として広げてお見せするもの、と思っていただければ幸いです。

pic_6.jpg

既存の優れた記事と、本記事の立ち位置

圏論とHaskellをめぐっては、すでに数多くの優れた記事が存在します。本題に入る前に、敬意を込めて、代表的なものに触れておきます。
 

そして、これらの記事には、ひとつの共通点があります。

いずれも「圏論という数学を、いかに正確にHaskellやHask圏で捉えるか」を説く、数学の側からの記事だということです。水準も高く、圏・関手・自然変換の定義を、前提として進みます。

本記事は、ちょうど逆の方向から書きます。

本記事が扱うのは、

  • 実務でコードを書くとき、頭の中で圏論がどう参照されるのか
  • 型シグネチャを見た瞬間、熟練のプログラマは何を考えているのか
  • ApplicativeかMonadか ── その使い分けは、効果が互いに独立かどうかで決まる

── という、実装の現場の思考そのものです。

筆者の知るかぎり、この「実装時の思考を言葉にする」という視点から、圏論的構造の全体を横断的に整理した記事は、まだ見かけません。

とはいえ、念のため申し添えます。Qiitaをはじめとする技術記事は、論文ではありません。

本記事執筆にあたり行った先行記事の調査は、厳密な先行記事の調査ではありません。
本記事執筆者が見つけられなかった、この記事と同じ視座からすでに書かれた記事が、すでにどこかにある可能性は、十分にあります。

もし同じ趣旨の先行記事をご存じでしたら、ご教示いただけますと幸いです。


0. 全体の見取り図 ── 型クラス階層という「圏論の地図」

Haskellの型クラス階層は、それ自体が圏論的構造の地図になっています。まず、その全体像を示します。

ct_typeclass_hierarchy.png

Functor        -- 箱の中身に関数を適用できる            fmap
  ↓ (制約を強める)
Applicative    -- 箱に入った関数を、箱に入った値に適用できる   <*>, pure
  ↓
Monad          -- 箱を返す関数を、つなげられる            >>=, return

Semigroup      -- 2つを1つに結合できる                <>
  ↓ (単位元を加える)
Monoid         -- 結合に加えて「空」がある              mempty

Foldable       -- 中身を1つの値に畳み込める             foldr, foldMap
  ↓
Traversable    -- 構造を保ったまま、効果を伴って走査できる    traverse, sequenceA

自然変換        -- ファンクタからファンクタへの、構造を保つ写像   例: [a] -> Maybe a
随伴           -- 2つの関手が「ゆるい逆」の対をなす         例: curry/uncurry, free/forgetful

この地図の各ノードを、上から順に、「何を解決する構造か」「型はどう読むか」「実務でいつ頭の中で参照されるか」の3点で見ていきます。


1. Functor ── 文脈を保ったまま、中身を写す

構造

class Functor f where
  fmap :: (a -> b) -> f a -> f b

fmap は、「a -> b という普通の関数」を、「f a -> f b という、文脈 f の中での関数」へ持ち上げる

圏論的には

Functorとは、圏から圏への、対象と射の対応であって、恒等射と合成を保つものです。
プログラミングの文脈に引きつければ、「型を型へ移し(af a へ)、関数を関数へ移し(gfmap g へ)、しかもその移しかたが id. を壊さない」── そういう性質だと言えます。

fmap id      = id              -- 恒等を保つ
fmap (g . h) = fmap g . fmap h -- 合成を保つ

この2つのFunctor則は、単なる数学的お作法ではありません。
fmap は中身だけをいじり、構造(箱の形)には触らない」という保証です。

リストに fmap をほどこしても、要素数は変わりません。Maybefmap をほどこしても、JustNothing かの別は変わりません。

この保証があるからこそ、安心して合成できるのです。

実務での参照のされ方

型に f a が現れ、その a の部分だけを変えたい ── そう思った瞬間に、fmap が頭に浮かびます。構造はそのまま、中身だけ。これが、最初の反射です。


2. Applicative ── 複数の文脈つき値を、合成する

ここが、入門の3つ(関数合成・ファンクタ・モナド)には含めない、しかし実務で極めて多用される構造です。

構造

class Functor f => Applicative f where
  pure  :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b

<*>(apと読みます)は、箱に入った関数を、箱に入った値に適用します。

なぜApplicativeが要るのか

Functorの fmap は、引数が1つの関数しか持ち上げられません。

fmap (+1) (Just 3)     -- Just 4   -- 1引数ならOK

では、2引数の関数を、2つの Maybe に適用したいときは?

-- (+) :: Int -> Int -> Int を、Just 3 と Just 5 に適用したい
fmap (+) (Just 3)      -- Just (3+) :: Maybe (Int -> Int)  ← 関数が箱に入ってしまう!

fmap で2引数関数を持ち上げると、「箱に入った関数Maybe (Int -> Int) ができてしまう。これを「箱に入った値」Just 5 に適用する手段が、<*> です。

(+) <$> Just 3 <*> Just 5      -- Just 8
(+) <$> Just 3 <*> Nothing     -- Nothing  (どれか1つでも欠ければ全体が欠ける)

<$>fmap の中置記法)

実務での参照のされ方

  • 複数のバリデーション結果を合成するときの定番。User <$> validateName n <*> validateAge a <*> validateEmail e ── どれか1つでも失敗すれば全体が失敗。しかも Validation を使えば全エラーを集約できる(ここがMonadと決定的に違う)。なぜ集約できるのかというと、ValidationApplicative インスタンスが、エラー型に Semigroup(後述)を要求し、複数のエラーを <> で連結するからです。Monadの >>= は「最初の失敗で短絡」してしまうため、この全エラー集約はApplicative特有の芸当です
     
  • パーサーコンビネータ<*> で「順に読む」を組む
     
  • 独立した複数の効果を、並べて合成する」とき。Monadより弱いが、その弱さゆえに並列評価・静的解析が可能

熟練者は、「この複数の効果は、互いに依存しているか?」を問います。

依存していなければ、Applicative(<*>)で十分かつ最適。
依存していて、「前の結果で次を決める」必要があるならMonad(>>=)。

この見極めが、ApplicativeとMonadの使い分けの核心です。


3. Monad ── 前の結果が、次の計算を決める

構造

class Applicative m => Monad m where
  (>>=) :: m a -> (a -> m b) -> m b
  return :: a -> m a

>>=(bind)は、「文脈つきの値 m a」と「値を受け取って文脈つきの値を返す関数 a -> m b」を、つなぐ。

(補足:return は、いまの Haskell(GHC 9.x 系)では Applicativepure と同一であり、Monad のメソッドとしての return は実質的に非推奨となる流れにあります。

本記事では、Applicative から Monad への話の連続性と、初学から上級への橋渡しの分かりやすさを優先して、あえて従来どおり return を併記しています。

実コードでは pure に統一して差し支えありません)

ApplicativeとMonadの本質的な違い

-- Applicative: 効果の構造が、値を見る前に決まっている
(+) <$> action1 <*> action2     -- action2 は action1 の結果に依存しない

-- Monad: 前の結果を見て、次の効果を決められる
action1 >>= \x ->
  if x > 0 then action2 else action3   -- action1 の結果 x で分岐できる

Monadは、前の計算結果に応じて、次の計算そのものを変えられます

この表現力ゆえに強力ですが、代償もあります。次に何が起きるかが、実行するまで分からないのです。
だからこそ、静的な解析や並列化は、Applicativeより難しくなります。

強さと、解析可能性は、トレードオフ。この感覚が、上級者の設計判断の土台にあります。

Monad則

return a >>= f   = f a              -- 左単位元
m >>= return     = m                -- 右単位元
(m >>= f) >>= g  = m >>= (\x -> f x >>= g)  -- 結合律

結合律は、「do 記法でどう区切っても同じ意味になる」という保証です。
これがなければ、do ブロックのリファクタリングが意味を変えてしまう。

実務での参照のされ方

型に a -> m b が並び、「前の結果で次を分岐したい」と思った瞬間、>>=do 記法が頭に浮かびます。Maybe(失敗の伝播)、Either(エラー情報つき)、State(状態)、IO(副作用)、Reader(環境)── すべて「文脈つき計算の逐次合成」というひとつの同じ絵に収まります。

ここにも、現場の脳内の、ささやかな転回があります。多くのプログラマにとって、Monadは「学んで理解するもの」として最初に現れるのではありません。たとえば、失敗するかもしれない処理を三つ、四つと数珠つなぎにし、if のネストが深くなり、エラーの伝播を手で書くのに疲れてきた、まさにその頃 ── 「これらの計算を、同じひとつの合成規則で、まとめて扱えないか」と願う瞬間が訪れます。

その願いに、ぴたりと応える形が、すでに用意されていた。それがMonadだった ── という順序です。つまり、複数の計算文脈を「同じ合成規則」に揃えたくなった瞬間、プログラマはMonadを"理解する"というより、"必要とし始める"。定義を覚えてから使うのではなく、必要に迫られた手が、Monadという形へ自然に手を伸ばす。圏論語彙は、そうやって、後から「あれに名前がついていたのか」と回収されていくのです。


4. Semigroup と Monoid ── 「まとめる」の代数

構造

class Semigroup a where
  (<>) :: a -> a -> a            -- 結合的な二項演算

class Semigroup a => Monoid a where
  mempty :: a                    -- 単位元
  • Semigroup:「2つを1つにまとめる」結合的な演算 <> を持つ
  • Monoid:それに加えて「何もしない基準点」mempty を持つ
[1,2] <> [3,4]     -- [1,2,3,4]   リストの連結。mempty = []
"ab" <> "cd"       -- "abcd"      文字列。mempty = ""
Sum 3 <> Sum 5     -- Sum 8       足し算。mempty = Sum 0
Product 3 <> Product 5  -- Product 15  掛け算。mempty = Product 1

なぜ実務で重要か

Monoidは「集約(aggregation)の抽象」です。合計、連結、マージ、最大・最小、ログの蓄積 ── これらはすべて「結合的にまとめる」操作であり、Monoidとして統一的に扱えます。

そして決定的なのは ── 結合的であること((a<>b)<>c = a<>(b<>c))が、並列化と分割統治を可能にする点です。

MapReduceの reduce は、まさにMonoidの結合律に立脚しています。

それゆえに、「この集約はMonoidか」と問うことは、「並列化できるか」と問うことに、ほぼ等しいのです。

実務での参照のされ方

「複数の結果を1つにまとめたい」と思った瞬間、「これはMonoidか?単位元は何か?結合的か?」を問う。foldMap と組み合わせて、「構造を畳んで集約する」を一行で書く。

pic_2_.jpg


5. Foldable と Traversable ── 構造を畳む・構造を裏返す

Foldable

class Foldable t where
  foldr   :: (a -> b -> b) -> b -> t a -> b
  foldMap :: Monoid m => (a -> m) -> t a -> m

Foldableは「中身を1つの値に畳み込める構造」です。
foldMap は、各要素をMonoidに変換して、<> でまとめる ── FoldableとMonoidが、ここで握手します。

foldMap Sum [1,2,3,4]        -- Sum 10
foldMap (\x -> [x,x]) [1,2]  -- [1,1,2,2]

Traversable ── 上級者の必需品にして、最も美しい構造

class (Functor t, Foldable t) => Traversable t where
  traverse  :: Applicative f => (a -> f b) -> t a -> f (t b)
  sequenceA :: Applicative f => t (f a) -> f (t a)

traverse は、「効果を伴う関数を、構造の各要素に適用しつつ、効果を外側にまとめあげる」。

具体例がいちばん雄弁です。

-- 各文字列を Maybe Int にパースしたい。1つでも失敗したら全体を Nothing に
traverse parseInt ["1","2","3"]   -- Just [1,2,3]
traverse parseInt ["1","x","3"]   -- Nothing

-- sequenceA は「箱の入れ子をひっくり返す」
sequenceA [Just 1, Just 2, Just 3]  -- Just [1,2,3]
sequenceA [Just 1, Nothing, Just 3] -- Nothing

[Maybe a](失敗するかものリスト)を Maybe [a](失敗するかもの、リスト)へ裏返す。この「構造と効果の順序を交換する」操作こそ、Traversableの心臓です。

実務での参照のされ方

「リストの各要素に、失敗しうる処理やIO、非同期処理を適用して、結果をまとめて受け取りたい」── この瞬間に、traverse が頭に浮かびます。手で for を回し、結果を集め、失敗を判定する。あの定型コードが、traverse 一発で消えるのです。mapMtraverse のMonad版。

ここで、現場の脳内を、一度のぞいてみましょう。

たとえば「ユーザーIDのリストを受け取り、各IDをAPIで問い合わせて、ユーザー情報のリストを返したい。ただし、ひとつでも問い合わせに失敗したら、全体を失敗にしたい」── そんな場面です。

手は、いったん for ループを書きかけます。

空のリストを用意して、ループで結果を append し、途中で失敗フラグを立てて……と書き進めたところで、ふと手が止まる。

「待てよ、これは ── 各要素に ID -> IO (Maybe User) を適用して、結果を IO (Maybe [User]) にまとめたいだけだ。

構造(リスト)と効果(IO・失敗)の順序を入れ替えている

これは traverse そのものではないか」。

そう気づいた瞬間、十数行の手続きが、traverse fetchUser userIds という一行へ畳まれます。

この「for を書きかけて、Traversableに気づいて、手を引っ込める」一瞬
── ここに、圏論的な思考が、実装の現場で爆(は)ぜる瞬間があります。

ApplicativeとTraversableが組むと、「効果つきのループ」が宣言的に書ける
── これが、上級者がループをほとんど書かない理由のひとつです。

pic_3.jpg


6. 自然変換 ── 箱から箱へ、中身を保って移す

構造

自然変換は、2つのファンクタ fg の間の、「中身の型を問わず一様に成り立つ」変換です。
Haskellでは、おおむねこの形の多相関数として現れます。

type f ~> g = forall a. f a -> g a   -- (~> は標準の演算子ではなく、自然変換を表す慣用記法)

safeHead :: [a] -> Maybe a       -- リストファンクタ → Maybeファンクタ
listToMaybe = safeHead

~> はHaskell標準の演算子ではなく、自然変換を表すためにしばしば用いられる慣用記法です。natural-transformation パッケージなどで定義されます)

[a] -> Maybe a は、a が何であろうと一様に動く。この「中身に依存しない、箱そのものの変換」が自然変換です。

自然性条件

fmap g . alpha = alpha . fmap g

「先に中身を変換してから箱を移す」のと、「先に箱を移してから中身を変換する」のとが、一致するということです。

safeHead で言えば、「リストの中身に g を適用してから先頭を取る」のと「先頭を取ってから g を適用する」のが同じ ── 当たり前に見えて、この一様性が、ライブラリの抽象化を支えています。

ここに、Haskellならではの美しい事実があります。

forall a. f a -> g a という型の関数は、この自然性条件を、自動的に満たします
プログラマが証明する必要はありません。

なぜなら、Haskellの採用する「パラメトリック多相」── 中身の型 a が何であるかを、関数が一切覗き見できないという性質 ── によって、自然性が型システムのレベルで保証されるからです。

これは Philip Wadler の名高い論文「Theorems for free!(ただで定理が手に入る)」が説く現象で、型を書いた、ただそれだけで、数学的な法則がついてくる

型に導かれてコードを書く者にとって、これほど心強い後ろ盾はありません。

実務での参照のされ方

  • モナド変換子のhoist/自然変換による層の入れ替えmmorphhoist
  • インタプリタパターン:Free Monadを、別のモナドへ「解釈」する foldFreeinterpret は、自然変換を与える行為そのもの
  • この箱を、あの箱に変えたい。中身はそのまま」と思ったら、それは自然変換を書いている

7. 随伴 ── 「ゆるい逆」の対

圏論の最も深い概念のひとつで、実務で明示的に意識する頻度は下がりますが、気づかぬうちに毎日使っている構造です。

カリー化という、最も身近な随伴

curry   :: ((a, b) -> c) -> (a -> b -> c)
uncurry :: (a -> b -> c) -> ((a, b) -> c)

「ペアを受け取る関数」と「引数を1つずつ受け取る関数」とが、1対1に対応します。
これは、積関手 (-, b) と 指数関手 (b -> -) の随伴の現れです。

(a, b) -> c      a -> (b -> c)

この同型(カリー同型)は、関数型プログラミングの部分適用そのものの理論的根拠。引数を1つずつ渡せるのは、この随伴があるからです。

その他の随伴

  • Free ⊣ Forgetful:Free MonadやFreeモノイド(リストはアルファベット上の自由モノイド)の背後
  • 自由構成と忘却:「最小限の構造を、ただで足す」操作と「構造を忘れる」操作の対

実務での参照のされ方

随伴を名指しで使う場面は多くありませんが、「この2つの操作は、なぜか対になっている」「この変換には、自然な逆向きがある」と感じたとき、その背後には、しばしば随伴がひそんでいます。

Lensのgetter/setter、curry/uncurry、Free/interpret ── 一見無関係なこれらが「随伴」という一語でつながる瞬間が、圏論を学ぶ醍醐味です。

pic_4.jpg


8. 全体を貫く一本の糸 ── 結局、圏論は何をしているのか

ここまでの構造を、一枚に圧縮します。

構造 解決する問い 型のシグネチャの肝 実務トリガー
Functor 中身だけ変えたい (a->b) -> f a -> f b f a を見て中身を変えたい
Applicative 独立した複数の効果を合成 f (a->b) -> f a -> f b 効果が互いに独立
Monad 前の結果で次を決める m a -> (a-> m b) -> m b 効果が前の結果に依存
Monoid まとめる・集約 a -> a -> a, 単位元 合計・連結・マージ・並列集約
Foldable 構造を1値に畳む (a->b->b) -> b -> t a -> b 集約して取り出す
Traversable 構造と効果を裏返す (a-> f b) -> t a -> f (t b) 効果つきループ
自然変換 箱を別の箱へ forall a. f a -> g a 構造そのものを変換
随伴 ゆるい逆の対 F a -> b ≅ a -> G b カリー化・自由構成

そして、これら全体を貫く一本の糸は、入門の3つ(関数合成・ファンクタ・モナド)と同じ、たったひとつの問いです。

「点(型)と矢印(関数)を、どうつなぐか」

  • Functor・Applicative・Monadは、「文脈つきの矢印を、どうつなぐか」の強さの三段階
  • Semigroup・Monoidは、「矢印の行き先どうしを、どうまとめるか
  • Foldable・Traversableは、「構造の中の矢印を、どう畳み、どう裏返すか
  • 自然変換は、「ファンクタという箱そのものを、どう移すか
  • 随伴は、「箱と箱の、対応そのもの

上級の関数型プログラマがコードを書くとき、頭の中には、この地図が一枚、常に広がっています。

型シグネチャを見た瞬間、「これはどの構造か」「強さはどこまで要るか(Monadは過剰ではないか、Applicativeで足りないか)」「集約はMonoidに乗るか」「このループはtraverseか」を、ほぼ無意識のうちに判別しています。

圏論は、その判別を支える「共通の言語」 です。

バラバラに見えていた便利な機能を、ひとつの構造体系として見わたす。

だからこそ、新しい型や新しいライブラリに出会っても、「これはこの地図のどこに当たるのか」と、即座に位置づけられるのです。

未知を、既知の構造の上に置ける ── これが、最も抽象的な数学が、最も実務的な設計力になる理由です。

pic_5.jpg


まとめ

 

  • 関数型プログラミングで参照される圏論的構造は、型クラス階層という地図として整理できる
     
  • Functor → Applicative → Monad は「文脈つき計算の合成」の強さの3段階。強さと解析可能性はトレードオフであり、その見極めが設計判断の核心
     
  • Semigroup・Monoid は「結合的なまとめ」の代数で、並列化・分割統治の理論的根拠
     
  • Foldable・Traversable は「畳む・裏返す」。traverse は効果つきループを宣言的に消す上級者の必需品
     
  • 自然変換は「箱そのものの変換」、随伴は「ゆるい逆の対」で、カリー化やインタプリタの背後にいる
     
  • すべては「点と矢印を、どうつなぐか」というひとつの問いの、異なる答えである
     

For international readers

This article examines how working programmers reference the mathematics of category theory — in the very moment of writing functional code.

Aimed at advanced Haskell programmers, it sets out a "map of thought": how concepts such as Functor, Monad, Applicative, Traversable, natural transformations and adjunctions surface in a programmer's consciousness as they type.

Its distinctive angle is not what category theory is, but how it is referenced in the act of implementation — a framing that, to the author's knowledge, occupies ground not previously charted in this form.

The eight structures are presented not as isolated definitions, but as one connected territory, all deriving from a single question: how do we connect objects and arrows ?


関連記事

本記事には、関数型・圏論ともに未学習のPythonプログラマ向けに、関数合成・ファンクタ・モナドの3つに絞って解説した入門記事を、別途公開する予定です。圏論やHaskellにこれから入門する方は、そちらから読まれると、本記事の「全地図」へなだらかにつながるはずです(公開しだい、本記事からもリンクします)。

また、論理プログラミング・制約プログラミングの系譜を扱った記事とあわせて読むと、「宣言型」という大きな見取り図の中で、関数型プログラミングと、その背後の圏論の位置づけが、より鮮明になります。


Étale Cohomology(エタール・コホモロジー)
• Qiita: https://qiita.com/etale_cohomology
• Zenn: https://zenn.dev/etalecohomology
• GitHub: https://github.com/EtaleCohomology
• X(旧 Twitter): https://x.com/Etale_Cohomo

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?