用語Free Monadpop up everynowandthen しばらくの間、誰もが彼らが何であるかを説明することなくそれらを使用/議論しているようです。だから、無料のモナドとは何ですか? (私はモナドとHaskellの基本に精通していると思いますが、カテゴリ理論の非常に大まかな知識しかありません。)
エドワード・クメットの答えは明らかに素晴らしい。しかし、それは少し技術的です。これは、おそらくよりアクセスしやすい説明です。
無料のモナドは、ファンクターをモナドに変える一般的な方法です。つまり、任意のファンクターf
Free f
はモナドです。一対の関数を取得する以外は、これはあまり役に立ちません
liftFree :: Functor f => f a -> Free f a
foldFree :: Functor f => (f r -> r) -> Free f r -> r
これらの最初のものは、あなたのモナドに「入る」ことを可能にし、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)
さらに簡単な答えがあります。モナドは、join :: m (m a) -> m a
によってモナドコンテキストが折りたたまれたときに「計算」されるものです(>>=
はx >>= y = join (fmap y x)
として定義できることを思い出してください)。これは、モナドが計算の連続チェーンを通じてコンテキストを運ぶ方法です。シリーズの各ポイントで、前の呼び出しからのコンテキストが次のもので折りたたまれるためです。
無料のモナドはすべてのモナドの法則を満たしますが、崩壊(計算など)は行いません。ネストされた一連のコンテキストを構築するだけです。そのような無料のモナド値を作成するユーザーは、これらのネストされたコンテキストで何かを行う責任があり、そのような構成のmeaningは後まで延期することができますモナド値が作成されました。
Free Monad(データ構造)は、Monad(クラス)に対するList(データ構造)のように、Monad(クラス)に対するものです。これは簡単な実装であり、後でコンテンツの結合方法を決定できます。
おそらく、Monadが何であるか、各Monadはfmap
+ join
+ return
またはbind
+ return
の特定の(Monad-lawに従う)実装が必要であることを知っているでしょう。
Functor(fmap
の実装)があると仮定しますが、残りは実行時に行われる値と選択に依存します。つまり、Monadプロパティを使用できるが、後でMonad関数を選択することを意味します。
これは、Free Monad(データ構造)を使用して行うことができます。これは、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)
Haskellの無料モナドはファンクターのリストです。比較:
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
に置き換えます。
ダイアグラムはこれを明確にしますが、簡単に描画する機能はありません!