私が見てきた用語無料モナドがポップアップし、すべての 今 と 、その後いくつかの時間のために、誰もがちょうど彼らが何であるかの説明を与えることなく、それらを議論/使用するようです。だから:無料のモナドは何ですか?(私はモナドとHaskellの基本に精通していると思いますが、カテゴリー理論については非常に大まかな知識しか持っていません。)
私が見てきた用語無料モナドがポップアップし、すべての 今 と 、その後いくつかの時間のために、誰もがちょうど彼らが何であるかの説明を与えることなく、それらを議論/使用するようです。だから:無料のモナドは何ですか?(私はモナドとHaskellの基本に精通していると思いますが、カテゴリー理論については非常に大まかな知識しか持っていません。)
回答:
エドワード・クメットの答えは明らかに素晴らしいです。しかし、それは少し技術的です。これはおそらくよりアクセスしやすい説明です。
無料のモナドは、ファンクタをモナドに変換する一般的な方法にすぎません。つまり、与えられたファンクタf
Free f
はモナドです。これは、2つの関数を取得する場合を除いて、あまり役に立ちません。
liftFree :: Functor f => f a -> Free f a
foldFree :: Functor f => (f r -> r) -> Free f r -> r
これらの1つ目ではモナドに「入る」ことができ、2つ目ではモナドから「出る」ことができます。
より一般的には、Xが追加のPを含むYである場合、「フリーX」は、何も追加することなくYからXに移動する方法です。
例:モノイド(X)は追加の構造(P)を持つセット(Y)であり、基本的には操作(加算のことを考えることができます)と特定のアイデンティティ(ゼロなど)があると述べています。
そう
class Monoid m where
mempty :: m
mappend :: m -> m -> m
今、私たちは皆リストを知っています
data [a] = [] | a : [a]
まあ、t
それ[t]
がモノイドであることがわかっているタイプを考えると
instance Monoid [t] where
mempty = []
mappend = (++)
そのため、リストはセット(またはHaskellタイプ)に対する「無料のモノイド」です。
さて、無料のモナドは同じ考えです。ファンクタを取り、モナドを返します。実際、モナドはエンドファンクターのカテゴリーではモノイドと見なすことができるため、リストの定義は
data [a] = [] | a : [a]
無料モナドの定義によく似ています
data Free f a = Pure a | Roll (f (Free f a))
そしてMonad
インスタンスはに類似性があるMonoid
リストのインスタンスを
--it needs to be a functor
instance Functor f => Functor (Free f) where
fmap f (Pure a) = Pure (f a)
fmap f (Roll x) = Roll (fmap (fmap f) x)
--this is the same thing as (++) basically
concatFree :: Functor f => Free f (Free f a) -> Free f a
concatFree (Pure x) = x
concatFree (Roll y) = Roll (fmap concatFree y)
instance Functor f => Monad (Free f) where
return = Pure -- just like []
x >>= f = concatFree (fmap f x) --this is the standard concatMap definition of bind
今、私たちは2つの操作を取得します
-- this is essentially the same as \x -> [x]
liftFree :: Functor f => f a -> Free f a
liftFree x = Roll (fmap Pure x)
-- this is essentially the same as folding a list
foldFree :: Functor f => (f r -> r) -> Free f r -> r
foldFree _ (Pure a) = a
foldFree f (Roll x) = f (fmap (foldFree f) x)
Free f a = Pure a | Roll (f (Free f a))
として見るのは興味深いと思いますFree f a = a + fa + ffa + ...
。つまり、「fは何度も適用されます」。次にconcatFree
(つまりjoin
)「fを任意の回数適用して(fを任意の回数適用してa)」とし、ネストされた2つのアプリケーションを1つに折りたたみます。そして>>=
、「fをaに何度も適用する」と「どのようにaから(bをfを何回も適用する)に取得する」という方法で、基本的に後者を前者のaの内部に適用し、ネストを折りたたみます。今私はそれを手に入れました!
concatFree
基本的にはjoin
?
これはさらに簡単な答えです。モナドは、モナドのコンテキストが折りたたまれたときに「計算」されるものですjoin :: m (m a) -> m a
(>>=
として定義できることを思い出してくださいx >>= y = join (fmap y x)
)。これは、モナドが一連の計算の連鎖を通じてコンテキストを運ぶ方法です。シリーズの各ポイントで、前の呼び出しのコンテキストが次の呼び出しで折りたたまれているためです。
無料のモナドを満たすすべてのモナドの法律が、任意の崩壊(すなわち、計算)を行いません。ネストされた一連のコンテキストを構築するだけです。そのような自由なモナド値を作成するユーザーは、それらのネストされたコンテキストで何かを行う責任があります。そのため、そのような合成の意味は、モナド値が作成されるまで延期できます。
フリーfooは、偶然にもすべての「foo」法則を満たす最も単純なものです。つまり、それはfooになるために必要な法則を正確に満たし、余分なものは何もないということです。
忘れっぽいファンクタは、あるカテゴリから別のカテゴリに移動するときに構造の一部を「忘れる」ものです。
ファンクタを考えるF : D -> C
とG : C -> D
、私たちが言うF -| G
、F
に左随伴であるG
、またはG
右随伴あるF
たびforallは、B:F a -> b
と同型であるa -> G b
矢印が適切なカテゴリどこから来ました、。
正式には、無料のファンクターは忘れっぽいファンクターに随伴している。
無料モノイド
簡単な例であるフリーモノイドから始めましょう。
いくつかのキャリアセットT
で定義されたモノイド、2つの要素を1つにまとめる2項関数f :: T → T → T
、およびunit :: T
を使用して、連想法則と恒等則を作成しますf(unit,x) = x = f(x,unit)
。
あなたはファンクタを作ることができますU
(彼らはマップを確認し、ある矢印がモノイド準同型あり、モノイドのカテゴリからunit
のunit
カテゴリーに他のモノイドに、そしてあなたが意味を変えずに、他のモノイドへのマッピングの前または後に構成できること)セット(矢印は単なる関数の矢印です)は、操作とを「忘れて」unit
、キャリアセットを提供します。
次に、F
セットのカテゴリから、このファンクタに隣接したままになっているモノイドのカテゴリにファンクタを定義できます。そのファンクタはセットにマッピングファンクタでa
モノイドに[a]
、unit = []
とmappend = (++)
。
これまでの例を確認するには、疑似ハスケルで:
U : Mon → Set -- is our forgetful functor
U (a,mappend,mempty) = a
F : Set → Mon -- is our free functor
F a = ([a],(++),[])
次に、F
無料で表示するにはU
、忘れっぽいファンクタに隣接していることを示す必要があります。つまり、前述のように、
F a → b
に同型である a → U b
ここで、のターゲットがモノイドF
のカテゴリにあることを思い出してくださいMon
。矢印はモノイド準同型であるため、のモノイド同型[a] → b
がの関数によって正確に記述できることを示す必要がありますa → b
ます。
Haskellでは、我々はに住んでいるこの側面を呼び出すSet
(ER、Hask
ちょうど、私たちはふりをすることをHaskellの種類のカテゴリが設定されている)foldMap
から専門ときに、Data.Foldable
リストにはタイプがありますMonoid m => (a → m) → [a] → m
。
これが付随的であることから続く結果があります。特に、忘れてしまった場合は無料で作成し、次に忘れた場合は、一度忘れた場合と同じように、これを使用してモナディック結合を作成できます。以来UFUF
〜U(FUF)
〜UF
、と私たちはから身元モノイド準同型に渡すことができます[a]
と[a]
、私たちのadjunctionを定義同型てからリスト同型があることを得る[a] → [a]
タイプの関数であり、a -> [a]
、これはリストのためだけのリターンです。
これらの用語のリストを次のように記述することで、これらすべてをより直接的に作成できます。
newtype List a = List (forall b. Monoid b => (a -> b) -> b)
無料モナド
だから無料モナドとはですか?
さて、私たちは前と同じことをします。矢印がモナド同型であるモナドのカテゴリから、矢印が自然な変換である内部関数のカテゴリまで、忘れっぽいファンクタUから始めます。それに。
では、これは通常使用されるフリーモナドの概念とどのように関連していますか?
何かがフリーのモナドであることを知っていると、Free f
からモナド準同型を与えることFree f -> m
は、から自然変換(ファンクタ同型)を与えることと同じ(同型)であることがわかりf -> m
ます。FをUに隣接さF a -> b
せるにはa -> U b
、同型でなければならないことに注意してください。ここでは、モナドをファンクタにマッピングしています。
Fは、ハッキングでパッケージでFree
使用するタイプと少なくとも同型free
です。
また、次のように定義することで、フリーリスト用の上記のコードにより厳密に類似した方法で構築できます。
class Algebra f x where
phi :: f x -> x
newtype Free f a = Free (forall x. Algebra f x => (a -> x) -> x)
Cofree Comonads
忘れっぽいファンクタが存在すると仮定して、それに隣接するものを調べることで、同様の何かを構築できます。cofreeファンクタは、単純に忘れっぽいファンクタの/ right adjoint /であり、対称性によって、何かがcofreeコモナドであることを知ることw -> Cofree f
は、からコナモ準同型を与えることがからの自然な変換を与えることと同じことであることを知ることと同じですw -> f
。
フリーモナド(データ構造)は、モナド(クラス)に対するリスト(データ構造)と同様にモナド(クラス)に対するものです。これは、コンテンツをどのように組み合わせるかを後で決定できる簡単な実装です。
あなたはおそらくモナドが何であるか、そして各モナドがfmap
+ join
+ return
またはbind
+の特定の(モナド法則に従う)実装を必要とすることを知っているでしょうreturn
。
Functor(の実装fmap
)があると仮定しますが、残りは実行時に行われた値と選択に依存します。つまり、モナドプロパティを使用できるようにしたいが、後でモナド関数を選択したいということです。
これは、Free Monad(データ構造)を使用して行うことができます。FreeMonad(データ構造)は、Functor(タイプ)をjoin
、リダクションというよりはむしろそれらのFunctorのスタックとなるようにラップします。
本物return
とjoin
あなたが使用したいが、今リダクション機能へのパラメータとして指定することができますfoldFree
。
foldFree :: Functor f => (a -> b) -> (f b -> b) -> Free f a -> b
foldFree return join :: Monad m => Free m a -> m a
タイプを説明するためにFunctor f
、Monad m
とb
で置き換えることができます(m a)
:
foldFree :: Monad m => (a -> (m a)) -> (m (m a) -> (m a)) -> Free m a -> (m a)
ハスケルフリーモナドはファンクタのリストです。比較:
data List a = Nil | Cons a (List a )
data Free f r = Pure r | Free (f (Free f r))
Pure
に類似しNil
、Free
に類似していますCons
。無料のモナドは、値のリストではなくファンクタのリストを格納します。技術的には、別のデータ型を使用して無料のモナドを実装できますが、どの実装も上記のものと同型である必要があります。
抽象構文木が必要なときはいつでも無料のモナドを使います。無料モナドの基本関数は、構文ツリーの各ステップの形状です。
誰かがすでにリンクしている私の投稿は、無料のモナドで抽象的な構文ツリーを構築する方法のいくつかの例を示しています
簡単な具体例が役立つと思います。ファンクタがあるとします
data F a = One a | Two a a | Two' a a | Three Int a a a
明らかでfmap
。次いでFree F a
葉タイプ有する木の種類であるa
ノードでタグ付けされているがOne
、Two
、Two'
とThree
。 One
-nodesには1つの子があり、Two
-およびTwo'
-nodesには2つの子があり、Three
-nodesには3 つの子があり、さらにでタグ付けされていInt
ます。
Free F
モナドです。 値を持つ単なる葉であるツリーにreturn
マップx
しますx
。 t >>= f
それぞれの葉を見て、それらを木に置き換えます。葉に価値がある場合、y
その葉を木に置き換えますf y
。
ダイアグラムでこれはより明確になりますが、簡単に描くための機能がありません!