ファンクターの不動点の抽象的な概念を理解したいと思いますが、Haskell での正確な実装とカタモルフィズムを理解するのにまだ苦労しています。
たとえば、「Category Theory for Programers」の書籍 -- 359 ページに従って、次の代数を定義するとします。
-- (Int, LiftF e Int -> Int)
data ListF e a = NilF | ConsF e a
lenAlg :: ListF e Int -> Int
lenAlg (ConsF e n) -> n + 1
lenAlg NilF = 0
カタモルフィズムの定義により、次の関数を ListF の固定小数点 (リスト) に適用して、その長さを計算できます。
cata lenAlg :: [e] -> Int
cata lenAlg = lenAlg . fmap (cata lenAlg) . unFix
2 つの混乱があります。まず、Haskell コンパイラは List がListFの不動点であることをどのように認識していますか? 私は概念的にそれを知っていますが、コンパイラはどのように知っていますか、つまり、List とすべて同じである別の List' を定義した場合、コンパイラは List' が ListF の不動点でもあると自動的に推論しないと思いますそれ?(私は驚くだろう)。
第 2 に、cata lenAlg の再帰的な性質により、常にデータ コンストラクターの外側のレイヤーを unFix して、ファンクターの内側のレイヤーを公開しようとします (ちなみに、この私の解釈は正しいですか?)。しかし、すでにリーフにいる場合、どうすればこの関数呼び出しを呼び出すことができるでしょうか?
fmap (cata lenAlg) Nil
例として、誰かが以下の関数呼び出しの実行トレースを書いて明確にするのを手伝ってもらえますか?
cata lenAlg Cons 1 (Cons 2 Nil)
明らかな何かが欠けている可能性がありますが、この質問が同様の混乱を共有する他の人々にとってまだ意味があることを願っています.
回答のまとめ
@nm は、Haskell コンパイラが Functor A が Functor B の不動点であることを理解するには、明示的である必要があることを指摘して、私の最初の質問に答えました。この場合、
type List e = Fix (ListF e)
@luqui と @Will Ness は、葉 (この場合は NilF) で fmap (cata lenAlg) を呼び出すと、fmap の定義により NilF が返されることを指摘しました。
-- f :: a -> b
fmap f (ConsF e a) = ConsF e (f b)
fmap f NilF = NilF
私の最初の(より大きな)質問に直接対処したので、@ nmの回答を受け入れますが、3つの回答すべてが気に入っています。ご協力ありがとうございました。