Yoneda
from Freeモナド
https://pursuit.purescript.org/packages/purescript-free/4.0.0/docs/Data.Yoneda
gpt-5.icon
Haskell における Yoneda lemma(ヨネダ補題) とその Yoneda表現(Yoneda embedding / Yoneda変換) は、関数型プログラミングにおいて 関手を「関数の形」によって表現する」強力なテクニック です。
抽象的な圏論の話から、実用的な Haskell コードの観点まで順に説明します。
米田の補題の圏論的な形
圏論では次のように表されます:
$ \text{Nat}(\text{Hom}(a, -), F) \cong F(a)
つまり:
「a からのHom関手(Hom(a, -))から F への自然変換は、F a の要素と一対一対応する」
2. Haskell での対応関係
Haskell では Hom(a, -) は「a -> x」型に対応します。
したがって、Yoneda 補題は次のように表現されます:
code:haskell
forall f. Functor f => (forall x. (a -> x) -> f x) ≅ f a
左辺が「a を関数で写して得られる f x の全体」、右辺が「直接 f a」。
これらが 同型(isomorphic) であるというのが Yoneda の本質です。
3. Haskellでの実装例
Yonedaラッパーの定義:
code:haskell
newtype Yoneda f a = Yoneda { runYoneda :: forall r. (a -> r) -> f r }
これは「f を関数 (a -> r) によって包み直したもの」です。
同型の変換
code:haskell
toYoneda :: Functor f => f a -> Yoneda f a
toYoneda fa = Yoneda (\f -> fmap f fa)
fromYoneda :: Yoneda f a -> f a
fromYoneda (Yoneda k) = k id
そして次が成立します:
code:haskell
fromYoneda . toYoneda = id
toYoneda . fromYoneda = id
4. 何がうれしいのか?
Yoneda 変換は理論的に美しいだけでなく、実用的に「効率化」や「抽象化」 に役立ちます。
(1) Functor の fmap を不要にする
fmap を都度実行せずに「関数合成として遅延的に積み重ねる」ことができます。
code:haskell
fmap f (fmap g xs)
-- 通常はリストを2回走査する
toYoneda xs
& fmap f
& fmap g
& fromYoneda
-- 1回の走査で済む!
→ 多重な fmap を合成できる最適化。
5. 双対の Coyoneda
Yoneda の双対として Coyoneda もよく使われます。
code:haskell
data Coyoneda f a = forall x. Coyoneda (x -> a) (f x)
これは「f が Functor でなくても fmap を後付けで扱える」構造。
ライブラリで言えば Free Functor の一形態です。
6. 直感的イメージ
Yoneda:Functor の中身を「関数として引き出す」
⇒ 「f a は、a -> r の形の写像を通して完全に表現できる」
Coyoneda:Functor の中身を「関数として包む」
⇒ 「f が Functor でなくても、関数を付ければ Functor っぽく扱える」
7. まとめ
table:_
観点 Yoneda Coyoneda
定義 (a -> r) -> f r exists x. (x -> a, f x)
効果 fmap 合成の最適化 非FunctorをFunctor化
圏論的意味 Hom(a, -) の像 Hom(-, a) の像