モナドはエンドファンクターのカテゴリーの単なるモノイドですが、何が問題なのですか?


723

誰が最初に次のことを言ったのですか?

モナドはエンドファンクターのカテゴリーの単なるモノイドですが、何が問題なのですか?

そして、それほど重要ではないメモで、これは本当ですか?もしそうなら、説明を提供できますか(願わくば、Haskellの経験があまりない人でも理解できると思います)。


14
「働く数学者のためのカテゴリー」を参照してください
Don Stewart

19
Haskellでモナドを使うためにこれを理解する必要はありません。実用的な観点から、それらは地下の配管を「状態」の周りを通過するための賢い方法にすぎません。
starblue

1
私もこの素晴らしいブログ投稿をここに追加したいと思います。stephendiehl.com / posts / monads.htmlこれは質問に直接答えるものではありませんが、私の意見では、StephenはHaskellのカテゴリとモナドを結び付けるという素晴らしい仕事をしています。上記の回答を読んだ場合、これは2つの見方を統一するのに役立ちます。
ベンフォード

3
より正確には、「任意のカテゴリCについて、その内部関数のカテゴリ[C、C]は、合成によって誘発されるモノイド構造を持っています。[C、C]のモノイドオブジェクトは、C上のモナドです。」-en.wikipedia.org/wiki/Monoid_%28category_theory%29から。カテゴリー理論におけるモナドの定義については、en.wikipedia.org / wiki / Monad_%28category_theory%29を参照してください。

1
@Dmitry ファンクタはカテゴリ間の関数であり、適切に動作するようにいくつかの制約があります。カテゴリCの内部関数は、Cからそれ自体への単なる関数です。Data.Functorは、Hask カテゴリの内部関数の型クラスです。カテゴリはオブジェクトと射で構成されているため、ファンクタは両方をマッピングする必要があります。Data.Functorのインスタンスfの場合、オブジェクト(ハスケル型)のマップはf自体であり、射(ハスケル関数)のマップはfmapです。
Matthijs 2016年

回答:


796

その特定の言い回しは、ジェームズ・アイリーによる、彼の非常に面白いブリーフ、不完全、そしてほとんど間違ったプログラミング言語の歴史からのものであり、彼はフィクションとしてそれをフィリップ・ワドラーに帰している。

元の引用は、カテゴリー理論の基礎となるテキストの1つである「働く数学者のためのカテゴリー」の Saunders Mac Laneによるものです。ここがコンテキストです。これが意味するところを正確に理解するのにおそらく最適な場所です。

しかし、私は刺します。元の文はこれです:

結局のところ、XのモナドはXの内部関数のカテゴリの単なるモノイドであり、製品×は内部関数の構成と単位関数によって識別されるユニットで置き換えられています。

ここのXはカテゴリーです。Endofunctorは、カテゴリーからそれ自体へのファンクターです(通常、関数型プログラマーに関する限り、すべて Functorのs です。これは、主に1つのカテゴリーのみを扱っているためです。タイプのカテゴリーですが、私は余談です)。しかし、「endofunctors on X」のカテゴリーである別のカテゴリーを想像できます。これは、オブジェクトが内部関数であり、射が自然変形であるカテゴリです。

そして、それらの内部関数の中で、それらのいくつかはモナドかもしれません。モナドはどれですか?厳密には、特定の意味でモノイドであるもの。モナドからモノイドへの正確なマッピングを詳しく説明する代わりに(Mac Laneは私が期待するよりもはるかに優れているため)、それぞれの定義を並べて比較します。

モノイドは...

  • セット、S
  • 演算、•:S×S→S
  • Sの要素、e:1→S

...これらの法律を満たす:

  • (•B)•C = A•(B•C) 、全てのために、AB及びCにおけるS
  • E•= A•E =、すべてのためにAS

モナドは...

  • 内部関数T:X→X(Haskellでは* -> *Functorインスタンスを持つ種類の型コンストラクター)
  • 自然な変換μ:T×T→T、ここで×はファンクター組成を意味します(μjoinHaskellで知られています)
  • 自然な変換η:I→T、ここでIXの恒等内関数です(ηreturnHaskellで知られています)

...これらの法律を満たす:

  • μ∘Tμ=μ∘μT
  • μ∘Tη=μ∘ηT= 1(恒等変換)

少し目を細めると、これらの定義の両方が同じ抽象概念のインスタンスであることがわかるでしょう。


21
説明に感謝し、プログラミング言語の概要、不完全、そしてほとんど間違った歴史の記事に感謝します。そこからかもしれないと思いました。プログラミングユーモアの最高の作品の1つです。
ローマンA.テイチャー、

6
@ジョナサン:モノイドの古典的な定式化では、×はセットのデカルト積を意味します。詳細については、en.wikipedia.org / wiki / Cartesian_productを参照してください。ただし、基本的な考え方は、S×Tの要素はペア(s、t)であり、ここでs t Sおよびt∈Tです。したがって、モノイド積のシグネチャ•:S×S-> Sは、このコンテキストでは、Sの 2つの要素を入力として受け取り、Sの別の要素を出力として生成する関数を意味します。
トムクロケット、

12
@TahirHassan-カテゴリ理論の一般性では、セットではなく不透明な「オブジェクト」を扱うため、「要素」のアプリオリな概念はありません。しかし、オブジェクトがセットであり、矢印が関数であるカテゴリセットについて考えると、任意のセットSの要素は、任意の1要素セットからSへの関数と1対1で対応しています。Sの要素e、関数fは1つだけです:1-> S、ここで1は任意の1要素セットです...(続き)
Tom Crockett

12
@TahirHassan 1要素セットは、それ自体が「ターミナルオブジェクト」のより一般的なカテゴリ理論的な概念の特殊化です。ターミナルオブジェクトは、他のオブジェクトからの矢印が1つだけあるカテゴリのオブジェクトです(確認できます)これはSetの 1要素のセットにも当てはまります。カテゴリ理論では、端末オブジェクトは単に1と呼ばれます。それらは同型まで一意であるため、それらを区別する意味はありません。これで、すべてのSについて「Sの要素」の純粋なカテゴリー理論による説明ができました。これらは1からSへの単なる矢です!
トムクロケット

7
@TahirHassan-これをHaskellの用語で表すためにS、型である場合、関数を作成するときにできることは、型のf :: () -> S特定の用語S(ある場合は、その「要素」)を選択して返すということです。それ...あなたは引数に関して実際の情報を与えられていないので、関数の振る舞いを変える方法はありません。したがってf、毎回同じものを返すだけの定数関数でなければなりません。()(「ユニット」)はカテゴリHaskの最終オブジェクトであり、そこに生息する値が1(非発散)であるのは偶然ではありません。
トムクロケット

532

直感的に、私は空想数学の語彙が言っていることは次のとおりだと思います:

モノイド

モノイドはオブジェクトのセット、及びそれらを組み合わせた方法です。よく知られているモノイドは次のとおりです。

  • 追加できる数
  • 連結できるリスト
  • 結合できるセット

さらに複雑な例もあります。

さらに、すべてのモノイドにはIDがあります。これは、 "no-op"要素であり、他のものと組み合わせても効果がありません。

  • 0 + 7 == 7 + 0 == 7
  • [] ++ [1,2,3] == [1,2,3] ++ [] == [1,2,3]
  • {} ユニオン {apple} == {apple} ユニオン {} == {apple}

最後に、モノイドは連想的でなければなりません。(オブジェクトの左から右の順序を変更しない限り、必要に応じて組み合わせの長い文字列を減らすことができます)追加はOK((5 + 3)+1 == 5+(3+ 1))、ただし減算は((5-3)-1!= 5-(3-1))ではありません。

モナド

ここで、特別な種類のセットとオブジェクトを組み合わせる特別な方法を考えてみましょう。

オブジェクト

セットに特別な種類の関数(関数)が含まれているとします。そして、これらの関数には興味深いシグネチャがあります。これらは、数値を数値に、または文字列を文字列に運びません。代わりに、各関数は2段階のプロセスで数値のリストに数値を渡します。

  1. 0以上の結果を計算する
  2. どういうわけか、それらの結果を単一の答えに結合します。

例:

  • 1-> [1](入力をラップするだけ)
  • 1-> [](入力を破棄し、何もないものをリストにラップします)
  • 1-> [2](入力に1を加え、結果をラップする)
  • 3-> [4、6](入力に1を追加し、入力に2を乗算して、複数の結果をラップします

オブジェクトを組み合わせる

また、関数を組み合わせる方法も特別です。関数を組み合わせる簡単な方法は合成です。上記の例を見て、各関数をそれ自体で合成してみましょう。

  • 1-> [1]-> [[1]](入力を2回ラップする)
  • 1-> []-> [](入力を破棄し、リストの何もないものを2回ラップします)
  • 1-> [2]-> [UH-OH!](リストに「1」を追加することはできません!)
  • 3-> [4、6]-> [UH-OH!](リストを1つ追加することはできません!)

型理論をあまり理解しなくても、ポイントは2つの整数を組み合わせて整数を取得できることですが、2つの関数を常に合成して同じ型の関数を取得できるとは限りません。(型を持つ関数A - > 構成されますが、A-> [A]ません)。

それでは、関数を組み合わせる別の方法を定義しましょう。これらの関数の2つを組み合わせる場合、結果を「二重ラップ」する必要はありません。

これが私たちの仕事です。2つの関数FとGを組み合わせる場合は、次のプロセスに従います(バインディングと呼ばれます)。

  1. Fから「結果」を計算しますが、結合しません。
  2. Gを各Fの結果に個別に適用した結果を計算し、結果のコレクションのコレクションを生成します。
  3. 2レベルコレクションをフラット化し、すべての結果を結合します。

例に戻り、関数を「バインド」するこの新しい方法を使用して、関数をそれ自体と結合(バインド)します。

  • 1-> [1]-> [1](入力を2回ラップする)
  • 1-> []-> [](入力を破棄し、リストの何もないものを2回ラップします)
  • 1-> [2]-> [3](1を追加してから、もう一度1を追加して、結果をラップします。)
  • 3-> [4,6]-> [5,8,7,12](入力に1を追加し、入力に2を乗算し、両方の結果を保持し、両方の結果に対してもう一度すべて実行して、最終結果をラップします結果はリストになります。)

関数を組み合わせるこのより洗練された方法は、関連性があります(ファンシーなラッピングを行っていないときの関数構成の関連性に続きます)。

それをすべて一緒に結ぶ、

  • モナドは、関数(の結果)を組み合わせる方法を定義する構造です。
  • モノイドがオブジェクトを組み合わせる方法を定義する構造であるのと同様に、
  • 結合の方法が結合的である場合、
  • そして、組み合わせることができる特別な「いいえ-OP」がある場合、何かにつながるために何か変わらないが。

ノート

結果を「ラップ」する方法はたくさんあります。リストやセットを作成したり、最初の結果を除いてすべて破棄したり、結果がないかどうかを確認したり、状態のサイドカーを添付したり、ログメッセージを印刷したりできます。

私は本質的なアイデアを直感的に理解することを期待して、定義を少し緩めました。

モナドがタイプa- > [a]の関数を操作するように主張することで、少し単純化しました。実際、モナドはタイプa-> mbの関数で機能しますが、一般化は技術的な詳細の一種であり、主要な洞察ではありません。


22
これは、すべてのモナドがどのようにカテゴリを構成するかについての良い説明です(クライスリカテゴリは、あなたがデモンストレーションしているものです-アイレンベルク-ムーアカ​​テゴリもあります)。しかし 2つのKleisli矢印a -> [b]を作成することはできませんc -> [d]b=の場合にのみこれを実行できますc)ため、これはモノイドを完全に記述していません。これは実際には、「モノイド演算子」である関数構成ではなく、説明した平坦化操作です。
Tom Crockett、2011

6
確かに、モナドを1種類のみに制限した場合、つまり、フォームのKleisli矢印のみを許可した場合a -> [a]、これはモノイドになります(Kleisliカテゴリを単一のオブジェクトに減らし、任意のカテゴリのオブジェクトを1つだけにするため)定義によりモノイドです!)、しかしそれはモナドの完全な一般性を捕らえません。
Tom Crockett

5
最後に、a-> [a]は単なる-> [] aであることを覚えておくと役立ちます。([]も単なる型コンストラクタです。)そして、それは-> mbとしてだけでなく、[]は実際にMonadクラスのインスタンスです。
Evi1M4chine 2013

8
これは、モナドと、私が文字通り数週間で遭遇したモノイドの数学的背景の最もよくわかりやすい説明です。これは、モナドに関しては、すべてのHaskellの本に印刷する必要があります。賛成!多分さらに情報を取得します、モナドはそれらに入れられたものを何でもhaskellにポストするパラメータ化されたtypeclassインスタンスとして実現されます。(少なくとも、これまでにそれらを理解した方法です。間違っている場合は修正してください。haskell.org/ haskellwiki / What_a_Monad_is_notを参照してください)
sjas

1
これは素晴らしいです-それは私が他の誰かにそれを説明できるほど十分に理解した唯一の説明です...しかし、これが何かを考えるための貴重な方法である理由がまだわかりません。:(
アダムバーンズ

84

まず、使用する拡張機能とライブラリ:

{-# LANGUAGE RankNTypes, TypeOperators #-}

import Control.Monad (join)

これらのうちRankNTypes、以下に絶対に不可欠な唯一のものです。私はかつてRankNTypes一部の人々が有用だと思ったとの説明を書いたので、それを参照します。

トムクロケットの素晴らしい答えを引用して、

モナドは...

  • 内部ファンクター、T:X-> X
  • 自然な変換μ:T×T-> T、ここで×はファンクター組成を意味します
  • 自然な変換、η:I-> T、ここでIXの恒等内関数です

...これらの法律を満たす:

  • μ(μ(T×T)×T))=μ(T×μ(T×T))
  • μ(η(T))= T =μ(T(η))

これをどのようにHaskellコードに変換しますか?さて、自然な変換の概念から始めましょう:

-- | A natural transformations between two 'Functor' instances.  Law:
--
-- > fmap f . eta g == eta g . fmap f
--
-- Neat fact: the type system actually guarantees this law.
--
newtype f :-> g =
    Natural { eta :: forall x. f x -> g x }

フォームのタイプはf :-> g関数のタイプに似ていますが、2つのタイプ(種類の)の間の関数として考えるのではなく、2つのファンクタ(それぞれの種類)の間のとして考えます。例:** -> *

listToMaybe :: [] :-> Maybe
listToMaybe = Natural go
    where go [] = Nothing
          go (x:_) = Just x

maybeToList :: Maybe :-> []
maybeToList = Natural go
    where go Nothing = []
          go (Just x) = [x]

reverse' :: [] :-> []
reverse' = Natural reverse

基本的に、Haskellでは、自然な変換は、あるタイプf xから別のタイプへの関数g xであり、xタイプ変数は呼び出し側から「アクセス不可能」です。したがって、たとえば、sort :: Ord a => [a] -> [a]インスタンス化できる型について「ピッキー」であるため、自然な変換に変換することはできませんa。私がこれを考えるのによく使用する直感的な方法の1つは次のとおりです。

  • ファンクタは、構造に触れずに何かのコンテンツを操作する方法です。
  • 自然な変化とは、コンテンツに触れたり、見たりせずに、何かの構造を操作する方法です。

さて、それが邪魔にならないように、定義の節に取り組みましょう。

最初の節は「内部関数T:X-> X」です。さて、FunctorHaskellのすべては、人々が「Haskカテゴリー」と呼ぶものの内部関数であり、そのオブジェクトはHaskell型(種類*)であり、その形態はHaskell関数です。これは複雑なステートメントのように聞こえますが、実際には非常に簡単なものです。それが意味すべてがあることがあることであるFunctor f :: * -> *あなたのタイプを構築する手段与えf a :: *いずれかのa :: *機能fmap f :: f a -> f bの任意の外にf :: a -> b、これらのオベイファンクタ法則という。

2番目の句:IdentityHaskell のファンクター(プラットフォームに付属しているため、インポートできる)は次のように定義されます。

newtype Identity a = Identity { runIdentity :: a }

instance Functor Identity where
    fmap f (Identity a) = Identity (f a)

したがって、自然変形η:I-> Tは、トムクロケットの定義から、次のように任意のMonadインスタンスに対して記述できますt

return' :: Monad t => Identity :-> t
return' = Natural (return . runIdentity)

3番目の条項:Haskellの2つのファンクターの構成は、このように定義できます(これはプラットフォームにも付属しています)。

newtype Compose f g a = Compose { getCompose :: f (g a) }

-- | The composition of two 'Functor's is also a 'Functor'.
instance (Functor f, Functor g) => Functor (Compose f g) where
    fmap f (Compose fga) = Compose (fmap (fmap f) fga)

したがって、Tom Crockettの定義による自然変換μ:T×T-> Tは、次のように書くことができます。

join' :: Monad t => Compose t t :-> t
join' = Natural (join . getCompose)

これがエンドファンクターのカテゴリーのモノイドであるという記述は、Compose(最初の2つのパラメーターのみに部分的に適用される)が結合的であること、およびそれIdentityがそのアイデンティティー要素であることを意味します。つまり、次の同型が成立します。

  • Compose f (Compose g h) ~= Compose (Compose f g) h
  • Compose f Identity ~= f
  • Compose Identity g ~= g

これらは両方ともとして定義されているためCompose、証明が非常に簡単です。また、Haskellレポートは、のセマンティクスを、定義されている型とのデータコンストラクターへの引数の型の間の同型として定義します。たとえば、証明してみましょう:IdentitynewtypenewtypenewtypeCompose f Identity ~= f

Compose f Identity a
    ~= f (Identity a)                 -- newtype Compose f g a = Compose (f (g a))
    ~= f a                            -- newtype Identity a = Identity a
Q.E.D.

ではNaturalnewtypeの、私は何を把握することはできません(Functor f, Functor g)制約がやっています。説明してもらえますか?
dfeuer 2015年

@dfeuer本質的に何もしていません。
Luis Casillas

1
@LuisCasillasこれらのFunctor制約は必要ないと思われるため、削除しました。同意しない場合は、自由に追加してください。
ラムダフェアリー

ファンクタの生成物が合成として解釈されることの正式な意味を詳しく説明できますか?特に、ファンクター構成の射射射は何ですか?私の推測では、積はそれ自体に対するファンクターFに対してのみ定義され、F x F joinが定義される場合のみです。そして、それjoinが射射射です。確信はないけど。
tksfz

6

注:いいえ、これは正しくありません。ある時点で、Dan Piponi自身からのこの回答に関するコメントがあり、ここでの原因と結果は正反対であり、James Iryの冗談に応えて彼の記事を書いたとのことです。しかし、おそらく一部の強迫的な整頓によって、それは削除されたようです。

以下は私の元の答えです。


それはIRYが読んでいたことは非常に可能ですモノイドからモナドに、ダンPiponi(SIGFPE)は圏論と「上endofunctorsのカテゴリの明示的な言及の多くの議論で、Haskellでモノイドからモナドを導出するポストHask」。いずれにせよ、モナドが内部ファンクターのカテゴリーでモノイドであることが何を意味するのか疑問に思う人なら誰でも、この派生を読むことで利益を得られるでしょう。


1
「恐らく強迫的なせいかもしれない」-または、このサイトで好意的に言及しているように、モデレーター:-)
18

6

私は、Mac Laneの「動作する数学者のためカテゴリー理論」からの悪名高い引用の推論をよりよく理解するためにこの投稿に来ました。

何かが何であるかを説明する際、そうでないものを説明することも同様に役立ちます。

Mac Laneがモナドを説明するために説明を使用しているという事実は、モナドに固有の何かを説明していることを意味するかもしれません。私と一緒に耐えなさい。声明をより広く理解するためには、彼がモナドに固有の何かを説明していないことを明確にする必要があると思います。この声明は、特にApplicativeとArrowsについて説明しています。同じ理由で、Int(SumとProduct)に2つのモノイドを持つことができます。endofunctorsのカテゴリでは、Xにいくつかのモノイドを持つことができます。しかし、類似点はさらにあります。

モナドとアプリケーションの両方が基準を満たしています:

  • 内部=>任意の矢印、または同じ場所で開始および終了する射
  • functor =>任意の矢印、または2つのカテゴリ間の射

    (例:毎日Tree a -> List b、ただしカテゴリ内Tree -> List

  • monoid =>単一のオブジェクト。つまり、単一のタイプですが、このコンテキストでは、外部レイヤーに関してのみです。そう、我々は持つことができないTree -> Listだけで、List -> List

ステートメントは「...のカテゴリ」を使用しますこれはステートメントの範囲を定義します。一例として、のFunctorカテゴリの範囲を説明しf * -> g *、すなわち、Any functor -> Any functor例えば、Tree * -> List *またはTree * -> Tree *

カテゴリステートメントで指定されていないものは何でもすべてが許可される場所を示します。

この場合、ファンクタ内では、* -> *別名a -> bは指定されていませんAnything -> Anything including Anything else。私の想像力がInt-> Stringにジャンプするので、それにはInteger -> Maybe Int、またはMaybe Double -> Either String Intwhere も含まれa :: Maybe Double; b :: Either String Intます。

したがって、ステートメントは次のようにまとめられます。

  • ファンクタスコープ :: f a -> g b(つまり、任意のパラメータ化された型から任意のパラメータ化された型へ)
  • エンド+ファンクタ:: f a -> f b(つまり、パラメータ化された1つの型から同じパラメータ化された型へ)...別の言い方をすると、
  • エンドファンクターのカテゴリーのモノイド

では、この構造の力はどこにあるのでしょうか?完全なダイナミクスを理解するために、モノイド(識別矢印のように見える単一のオブジェクト)の典型的な描画が、任意の数のモノイド値で:: single object -> single objectパラメーター化された矢印の使用が許可されていることを説明できないことを確認する必要がありました。Monoidで許可されている1つのタイプのオブジェクトから。等価の内部、〜識別矢印の定義で、ファンクターの型の値と、最も内側の「ペイロード」レイヤーの型と値の両方が無視されます。したがって、関数型が一致するすべての状況で等価性が返されます(たとえば、両方がであるため、と同等です)。trueNothing -> Just * -> NothingJust * -> Just * -> Just *Maybe -> Maybe -> Maybe

サイドバー:〜外は概念的なものですが、の左端の記号ですf a。また、「Haskell」が最初に読み込む内容(全体像)についても説明します。そのため、TypeはType値に対して「外側」になります。プログラミングにおけるレイヤー間の関係(参照のチェーン)は、カテゴリーで関連付けるのは容易ではありません。セットのカテゴリは、ファンクタのカテゴリ(パラメータ化されたタイプ)を含むタイプ(Int、文字列、多分Intなど)を記述するために使用されます。参照チェーン:Functor Type、Functor値(そのFunctorのセットの要素、たとえば、Nothing、Just)、および各ファンクター値が指す他のすべて。カテゴリでは、関係の記述方法が異なります。たとえば、return :: a -> m aあるFunctorから別のFunctorへの自然な変換と見なされ、これまでに述べたものとは異なります。

メインスレッドに戻ると、すべての定義済みテンソル積とニュートラル値について、ステートメントは、その逆説的な構造から生まれた驚くほど強力な計算構造を説明することになります。

  • 外側では単一のオブジェクトとして表示されます(例:):: List。静的
  • しかし内部では、多くのダイナミクスが可能です
    • 任意のアリティの関数への飼料として、同じタイプの任意の数の値(たとえば、Empty |〜NonEmpty)。テンソル積は、任意の数の入力を単一の値に減らします...外部層の場合(foldペイロードについては何も言わない)
    • 最内層のタイプと値の両方の無限範囲

Haskellでは、ステートメントの適用可能性を明確にすることが重要です。この構造の能力と汎用性は、モナド自体とはまったく関係ありません。言い換えれば、構造はモナドをユニークにするものに依存しません。

互いに依存する計算をサポートするために共有コンテキストでコードを構築するかどうか、並列に実行できる計算を比較するかどうかを検討する場合、この悪名高いステートメントは、説明されているとおり、選択の違いではありませんApplicative、Arrows、Monadsですが、どれだけ同じかを説明しています。目前の決定については、陳述は意味がない。

これはよく誤解されています。声明はjoin :: m (m a) -> m aさらに、モノイドの内部ファンクターのテンソル積として記述されています。ただし、このステートメントのコンテキストで、どのように(<*>)選択することもできるかについては明確に述べていません。それは本当に6分の6の例です。値を組み合わせるロジックはまったく同じです。同じ入力はそれぞれから同じ出力を生成します(Intを組み合わせるときに異なる結果を生成するため、IntのSumおよびProductモノイドとは異なります)。

つまり、要約すると、エンドファンクターのカテゴリーのモノイドは、

   ~t :: m * -> m * -> m *
   and a neutral value for m *

(<*>)そして、(>>=)の両方が2つのへの同時アクセスを提供m単一の戻り値を計算するために値を。戻り値の計算に使用されるロジックはまったく同じです。それは彼らが(パラメータ化機能の異なる形状がなければf :: a -> bk :: a -> m b)と計算の同じ戻り値の型とパラメータの位置(すなわち、a -> b -> bb -> a -> bごとに、それぞれ)、私は、我々はmonoidalロジックをパラメータ化している可能性が疑われます両方の定義で再利用するためのテンソル積。ポイントを作るための練習として、試してみて、実装~t、およびあなたが終わる(<*>)と、(>>=)あなたはそれを定義することを決定した方法に応じてforall a b

私の最後の点が少なくとも概念的に真である場合、それはApplicativeとMonadの間の正確で計算上の違いのみを説明します:それらがパラメーター化する関数。言い換えれば、違いはこれらの型クラスの実装の外部にあります。

結論として、私自身の経験では、Mac Laneの悪名高い引用は素晴らしい「後藤」ミームを提供しました。これは、Haskellで使用されているイディオムをよりよく理解するためにカテゴリをナビゲートするときに参照する道標です。Haskellで見事にアクセス可能になった強力なコンピューティング能力の範囲をキャプチャすることに成功しました。

しかし、モナド外でのステートメントの適用性を最初に誤解した方法と、ここで伝えたいことには皮肉があります。それが説明するすべてのものは、ApplicativeとMonads(およびとりわけArrows)の間で類似しているものであることがわかります。それが言っていないことは、正確にはそれらの間の小さいが有用な区別です。

-E


5

ここでの答えはモノイドとモナドの両方を定義するのに優れた仕事をしますが、それでも質問に答えるようには見えません:

そして、それほど重要ではないメモで、これは本当ですか?もしそうなら、説明を提供できますか(願わくば、Haskellの経験があまりない人でも理解できると思います)。

ここで欠落している問題の核心は、「モノイド」の別の概念、より正確にはいわゆるカテゴリー化、つまりモノイドカテゴリのモノイドの概念です。悲しいことに、Mac Laneの本自体が非常に混乱しています

X結局のところ、モナドはの内部関数のカテゴリのモノイドにすぎず、X製品×は内部関数の構成と単位関数によって識別されるユニットに置き換えられています。

主な混乱

なぜこれが混乱するのですか?それはの「内部ファンクターのカテゴリーのモノイド」が何であるかを定義していないからXです。代わりに、この文は、すべての内部ファンクターのセットモノイドを、ファンクター構成をバイナリ演算として、アイデンティティファンクターをモノイド単位として取得することを示唆しています。これは完全に正常に機能し、アイデンティティファンクターを含み、ファンクター構成で閉じられているエンドファンクターのサブセットのモノイドに変わります。

しかし、これは正しい解釈ではありません。この段階では、この本では明確にされていません。モナドf固定された内部ファンクターであり、構成の下で閉じられた内部ファンクターのサブセットではありません。一般的な構成は、使用するfために生成するすべてのセット取ることによってモノイドをk倍組成物f^k = f(f(...))f含む、それ自体と、k=0アイデンティティへのその対応しますf^0 = id。そして今、Sこれらすべてのすべての力のセットk>=0は、確かに「製品×内部ファンクターの構成とアイデンティティ内部ファンクターによるユニットセットで置き換えられた」モノイドです。

それでも:

  • このモノイドSは、任意のファンクタに対して、fまたは文字通りの任意の自己マップに対しても定義できますX。によって生成されるモノイドfです。
  • Sファンクタ構成と恒等ファンクタによって与えられるモノイド構造はf、モナドであるかどうかとは関係ありません。

さらに、混乱を招くために、「モノイドカテゴリのモノイド」の定義は、目次からわかるように、本の後半にあります。そして、この概念を理解することは、モナドとの関係を理解するために絶対的に重要です。

(厳格)モノイドカテゴリ

(モナドに後の章VIより付属)モノイド章VIIに行く、我々は、いわゆるの定義見つける厳密モノイド圏トリプルなどを(B, *, e)、ここでBカテゴリ、あるbifunctor固定他の成分と各成分に対する(ファンクタは)はの単位オブジェクトであり、結合性と単位法則を満たしています。*: B x B-> BeB

(a * b) * c = a * (b * c)
a * e = e * a = a

任意のオブジェクトのためa,b,cB、および任意の射で同じアイデンティティa,b,cを持つeに置き換えid_eのアイデンティティ射、e。関心のある私たちの場合、射としての自然変換BX伴うの内部*ファンクター、ファンクター組成およびe恒等ファンクターがどこにあるかを観察することは、直接検証できるように、これらのすべての法則が満たされていることを観察することは現在有益です。

この本の後に続くのは、「緩和された」モノイドカテゴリの定義です。この法則では、いわゆるコヒーレンス関係を満たすいくつかの固定された自然変換のみを法として保持しますが、エンドファンクターカテゴリの場合は重要ではありません。

モノイドカテゴリのモノイド

最後に、第VII章のセクション3「モノイド」では、実際の定義が示されています。

cモノイドカテゴリのモノイドは、2つの矢印(射)(B, *, e)B持つオブジェクトです。

mu: c * c -> c
nu: e -> c

3つの図を可換にします。我々の場合には、これらは正確に対応する自然変換されendofunctorsのカテゴリで射、あることを思い出してくださいjoinreturnモナドのため。私たちは、組成物は作るとき、接続がより明確になっ*置き換える、より明確c * cc^2、どこc私たちのモナドです。

最後に、3つの可換図(モノイドカテゴリのモノイドの定義)は一般的な(厳密でない)モノイドカテゴリに対して記述されていますが、私たちの場合、モノイドカテゴリの一部として発生するすべての自然変換は実際には同一です。これにより、図はモナドの定義の図とまったく同じになり、対応が完全になります。

結論

要約すると、どのモナドも定義上は内部関数であり、したがって内部関数のカテゴリのオブジェクトです。モナドjoinreturn演算子は、その特定の(厳密な)モノイドカテゴリのモノイドの定義を満たします。逆に、エンドファンクターのモノイドカテゴリのモノイドは、定義上(c, mu, nu)、オブジェクトと2つの矢印で構成されるトリプルです。たとえば、この場合は自然変形であり、モナドと同じ法則を満たします。

最後に、(古典的な)モノイドとモノイドカテゴリのより一般的なモノイドの主な違いに注意してください。2つの矢印munu上記もうバイナリ操作とセット単位ではありません。代わりに、1つの固定エンドファンクターがありcます。本の*混乱した発言にもかかわらず、ファンクタの構成とアイデンティティファンクタだけでは、モナドに必要な完全な構造を提供しません。

別のアプローチは、標準的なモノイドと比較することであろうCセットの全ての自己マップのAバイナリ操作は、標準デカルト積をマッピングするために見ることができる組成物である、C x CC。分類されたモノイドに渡すと、デカルト積xをファンクタ構成*で置き換えます。バイナリ演算は、演算子のコレクションであるto muから c * cへの自然変換に置き換えられますcjoin

join: c(c(T))->c(T)

すべてのオブジェクトT(プログラミングのタイプ)。そして、古典的なモノイドの恒等要素は、固定された1点セットからのマップの画像で識別でき、return演算子のコレクションで置き換えられます

return: T->c(T) 

しかし、現在、デカルト積はなくなっているため、要素のペア、したがって二項演算はありません。


では、質問の「これは本当ですか」の部分に対するあなたの答えは何ですか?モナドがエンドファンクターのカテゴリーのモノイドであることは本当ですか?そして、もしそうなら、モノイドのカテゴリー理論の概念と代数モノイド(連想乗算と単位のセット)の間の関係は何ですか?
Alexander Belopolsky
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.