私は圏論のモナドについて読んでいます。モナドの定義の1つは、随伴関手のペアを使用します。モナドは、これらのファンクターを使用したラウンドトリップによって定義されます。圏論ではどうやら随伴は非常に重要ですが、随伴関手という観点からのハスケルモナドの説明は見たことがありません。誰かがそれを考えましたか?
編集:楽しみのために、これを正しく実行します。以下に保存されている元の回答
Category-extrasの現在の随伴コードは、随伴パッケージに含まれています: http://hackage.haskell.org/package/adjunctions
州のモナドを明示的かつ簡単に処理します。このコードは、transformersパッケージのData.Functor.Compose
を使用しますが、それ以外は自己完結型です。
F(D-> C)とg(C-> D)の間の随伴、f と書かれているgは、いくつかの方法で特徴付けることができます。 counit/unit(epsilon/eta)の説明を使用します。これは、2つの自然変換(ファンクター間の射)を示します。
class (Functor f, Functor g) => Adjoint f g where
counit :: f (g a) -> a
unit :: a -> g (f a)
コユニットの「a」は実際にはCのアイデンティティファンクターであり、ユニットの「a」は実際にはDのアイデンティティファンクターであることに注意してください。
コユニット/ユニット定義からhom-set随伴定義を復元することもできます。
phiLeft :: Adjoint f g => (f a -> b) -> (a -> g b)
phiLeft f = fmap f . unit
phiRight :: Adjoint f g => (a -> g b) -> (f a -> b)
phiRight f = counit . fmap f
いずれにせよ、次のようにユニット/コユニットの随伴関手からモナドを定義できるようになりました。
instance Adjoint f g => Monad (Compose g f) where
return x = Compose $ unit x
x >>= f = Compose . fmap counit . getCompose $ fmap (getCompose . f) x
これで、(a、)と(a->)の間の古典的な随伴を実装できます。
instance Adjoint ((,) a) ((->) a) where
-- counit :: (a,a -> b) -> b
counit (x, f) = f x
-- unit :: b -> (a -> (a,b))
unit x = \y -> (y, x)
そして今、タイプの同義語
type State s = Compose ((->) s) ((,) s)
そして、これをghciにロードすると、Stateがまさに私たちの古典的な州のモナドであることを確認できます。反対の構成を取り、Costate Comonad(別名ストアcomonad)を取得できることに注意してください。
この方法でモナドにすることができる他の随伴((Bool、)ペアなど)はたくさんありますが、それらは一種の奇妙なモナドです。残念ながら、Haskellで直接ReaderとWriterを誘導する随伴を快適な方法で行うことはできません。私たちはできますContを実行しますが、コパンプキンが説明するように、反対のカテゴリからの随伴を必要とするため、実際にはいくつかの矢印を反転させる「随伴」型クラスの異なる「形式」を使用します。そのフォームは、随伴パッケージの別のモジュールにも実装されています。
この資料は、The Monad Reader13のDerekElkinsの記事-圏論によるモナドの計算:---(http://www.haskell.org/wikiupload/8/85/TMR-Issue13)で別の方法で説明されています。 pdf
また、Hinzeの最近のKan Extensions for Program Optimizationの論文では、Mon
とSet
の間の随伴からリストモナドの構築について説明しています。 http://www.cs.ox。 ac.uk/ralf.hinze/Kan.pdf
古い答え:
2つの参照。
1)カテゴリーエクストラは、いつものように、随伴関手とそれらからモナドがどのように発生するかを表現します。いつものように、考えるのは良いことですが、ドキュメントについてはかなり軽いです: http://hackage.haskell.org/packages/archive/category-extras/0.53.5/doc/html/Control-Functor-Adjunction .html
2)-カフェはまた、随伴関手の役割についての有望であるが簡単な議論を提供します。そのうちのいくつかはcategory-extrasの解釈に役立つかもしれません: http://www.haskell.org/pipermail/haskell-cafe/2007-December/036328.html
デレク・エルキンスは最近、夕食時に、(_ -> k)
共変ファンクターをそれ自体で構成することから、コントモナドがどのように発生するかを示していました。それがあなたがそれから(a -> k) -> k
を得る方法です。ただし、その共同ユニットは二重否定の排除につながり、Haskellでは記述できません。
これを説明および証明するいくつかのAgdaコードについては、 http://hpaste.org/68257 を参照してください。
これは古いスレッドですが、質問が面白いと思ったので、自分で計算をしました。うまくいけば、バルトスはまだそこにいて、これを読むかもしれません。
実際、この場合、アイレンバーグ-ムーア構造は非常に明確な状況を示しています。 (私は構文のようなHaskellでCWM表記を使用します)
T
をリストモナド_< T,eta,mu >
_(_eta = return
_および_mu = concat
_)とし、T代数_h:T a -> a
_を考えます。
(_T a = [a]
_は自由モノイド<[a],[],(++)>
、つまりアイデンティティ_[]
_と乗算_(++)
_であることに注意してください。)
定義上、h
は_h.T h == h.mu a
_および_h.eta a== id
_を満たす必要があります。
さて、いくつかの簡単な図の追跡は、h
が実際に(_x*y = h[x,y]
_で定義される)にモノイド構造を誘導し、h
がこの構造のモノイド準同型になることを証明します。
逆に、Haskellで定義されているモノイド構造_< a,a0,* >
_は、自然にT代数として定義されます。
このようにして(h = foldr ( * ) a0
、 '_(:)
_を_(*)
_に置き換え、_[]
_を_a0
_、IDにマップする関数)。
したがって、この場合、T代数のカテゴリは、HaskMon、HaskMonで定義可能なモノイド構造のカテゴリにすぎません。
(T代数の射が実際にモノイド準同型であることを確認してください。)
また、Grpの無料製品、CRngの多項式環などのように、リストをHaskMonのユニバーサルオブジェクトとして特徴付けます。
上記の構文に対応する形容詞は_< F,G,eta,epsilon >
_です。
どこ
F:Hask -> HaskMon
_、これはタイプaを 'a
によって生成された自由モノイド'、つまり_[a]
_、G:HaskMon -> Hask
_、忘却関手(掛け算を忘れる)、eta:1 -> GF
_、_\x::a -> [x]
_によって定義される自然変換、epsilon: FG -> 1
_、上記の折りたたみ関数によって定義された自然変換(自由モノイドからその商モノイドへの「正準全射」)次に、別の「クライスリ圏」とそれに対応する随伴関手があります。射が_a -> T b
_のHaskellタイプのカテゴリであり、その合成がいわゆる「クライスリ合成」_(>=>)
_によって与えられていることを確認できます。典型的なHaskellプログラマーは、このカテゴリーをよりよく知っているでしょう。
最後に、CWMに示されているように、T代数のカテゴリー(またはKleisliカテゴリー)は、適切な意味でリストモナドTを定義する裁定のカテゴリーの終末(または初期)オブジェクトになります。
二分木ファンクターT a = L a | B (T a) (T a)
についても同様の計算を行って、理解を確認することをお勧めします。
アイレンバーグ・ムーアによるモナドの補助ファンクターの標準的な構造を見つけましたが、それが問題に洞察を追加するかどうかはわかりません。構造の2番目のカテゴリは、T代数のカテゴリです。代数は、最初のカテゴリに「製品」を追加します。
では、リストモナドではどのように機能するのでしょうか。リストモナドのファンクターは、型コンストラクターで構成されています(例:Int->[Int]
および関数のマッピング(例:リストへのマップの標準的なアプリケーション)。代数は、リストから要素へのマッピングを追加します。 1つの例は、整数のリストのすべての要素を追加(または乗算)することです。ファンクターF
は、Intなどの任意の型を取り、それをIntのリストで定義された代数にマップします。ここで、積はモナド結合によって定義されます(または、その逆の場合、結合は積として定義されます)。忘却関手G
は代数を取り、積を忘れます。次に、随伴関手のペアF
、G
を使用して、通常の方法でモナドを構築します。
私は賢くないと言わなければなりません。
興味がある場合は、プログラミング言語でのモナドと随伴関手の役割に関する専門家ではない人の考えをいくつか示します。
まず第一に、与えられたモナドT
には、T
のKleisliカテゴリへの一意の随伴が存在します。 Haskellでは、モナドの使用は主にこのカテゴリの操作に限定されています(これは本質的に自由代数のカテゴリであり、商はありません)。実際、Haskellモナドでできることは、do式や_a->T b
_などを使用して、タイプ_(>>=)
_のいくつかのクライスリ射を作成して新しい射を作成することだけです。この文脈では、モナドの役割は表記法の経済性だけに制限されています.1つは射の結合法則を利用して_[0,1,2]
_の代わりに_(Cons 0 (Cons 1 (Cons 2 Nil)))
_を書くことができます、つまり、シーケンスを書くことができますツリーとしてではなく、シーケンスとして。
IOモナドの使用でさえ必須ではありません。現在のHaskell型システムは、データのカプセル化(既存の型)を実現するのに十分強力だからです。
これはあなたの最初の質問に対する私の答えですが、Haskellの専門家がこれについて何と言っているのか興味があります。
一方、すでに述べたように、モナドと(T-)代数の随伴関手の間にも1-1の対応があります。随伴関手は、マクレーンの用語では、「圏同値を表現する方法」です。随伴関手の典型的な設定_<F,G>:X->A
_では、F
はある種の「自由代数ジェネレーター」であり、Gは「忘却関手」であり、対応するモナドは(T代数を使用して)どのように(そしていつ) A
の代数的構造は、X
のオブジェクト上に構築されます。
HaskとリストモナドTの場合、T
が導入する構造はモノイドの構造であり、これはモノイドの理論が提供する代数的方法を通じてコードのプロパティ(正確さを含む)を確立するのに役立ちます。たとえば、関数foldr (*) e::[a]->a
は、<a,(*),e>
がモノイドである限り、連想演算と見なすことができます。これは、コンパイラが計算を最適化するために利用できる事実です(たとえば、並列処理)。別のアプリケーションは、「関数型プログラミングのgoto」Y(任意の再帰コンビネータ)を(部分的に)破棄することを期待して、カテゴリカルメソッドを使用して関数型プログラミングの「再帰パターン」を識別および分類することです。
どうやら、この種のアプリケーションは、圏論(MacLane、Eilenbergなど)の作成者の主な動機の1つです。つまり、圏の自然な同等性を確立し、ある圏のよく知られた方法を別の圏に移します(例:位相空間への同値法、プログラミングへの代数法など)。ここで、随伴関手とモナドは、このカテゴリーのつながりを利用するために不可欠なツールです。 (ちなみに、モナド(およびその双対のコモナド)の概念は非常に一般的であるため、Haskellタイプの「コホモロジー」を定義することさえできます。しかし、私はまだ考えていません。)
あなたが言及した非決定論的関数に関しては、私が言うことははるかに少ないです...しかし、注意してください。あるカテゴリーA
の随伴関手_<F,G>:Hask->A
_がリストモナドT
を定義する場合、一意の '比較ファンクター' _K:A->MonHask
_(Haskellで定義可能なモノイドのカテゴリー)が必要です。CWMを参照してください。これは、事実上、リストモナドを定義するために、関心のあるカテゴリが何らかの制限された形式のモノイドのカテゴリである必要があることを意味します(たとえば、いくつかの商が不足しているが、自由代数がない場合があります)。
最後に、いくつかの意見:
前回の投稿で言及した二分木ファンクターは、任意のデータ型_T a1 .. an = T1 T11 .. T1m | ...
_に簡単に一般化できます。つまり、Haskellのすべてのデータ型は、(対応する代数のカテゴリとKleisliカテゴリとともに)モナドを自然に定義します。これは、Haskellのデータコンストラクタが合計された結果です。これが、HaskellのMonadクラスがシンタックスシュガーにすぎないと私が考えるもう1つの理由です(もちろん、これは実際にはかなり重要です)。