私はそれがどのように機能するかを理解するためにライターモナドの例を見ていました、そしてそれらのほとんどすべてはリストライターモナドのように見えます。リストライターモナドはライターモナドの一種であることを私は知っています。しかし、素人の言葉で言えば、作家のモナドとは何ですか。
簡単に言うと、ライターモナドは、値を生成しながらアイテムを「ログ」に「書き込む」ことができるモナドです。完了すると、作成した値と、作成したすべてのものを含むログが作成されます。別の言い方をすれば、副作用が「ログへの書き込み」であるモナドです。
リストライターと(一般的な)ライターモナドの両方の例を使用して、これをより具体的にしましょう。ここではHaskellを使用します。これは、関数型プログラミングのモナドが説明された元のコンテキストだからです。
「リストライター」モナドは、アイテム(もちろん、あるw
タイプの[w]
)をアイテムのリスト(もちろん、タイプの)に記録するモナドだと思います。また、タイプの値を生成しますa
。(このコードを自分で使用してエラーが発生した場合は、下部の注を参照してください。)
newtype ListWriter w a = ListWriter { runListWriter :: ([w], a) }
instance Monad (ListWriter w) where
return a = ListWriter ([], a) -- produce an a, don't log anything
ListWriter (ws, a) >>= k =
let ListWriter (xs, a') = k a -- run the action 'k' on the existing value,
in ListWriter (ws ++ xs, a') -- add anything it logs to the existing log,
-- and produce a new result value
-- Add an item to the log and produce a boring value.
-- When sequenced with >>, this will add the item to existing log.
tell :: w -> ListWriter w ()
tell w = ListWriter ([w], ())
ex1 :: ListWriter String Int
ex1 = do
tell "foo"
tell "bar"
return 0
(注:これはex1 = tell "foo" >> tell "bar" >> return 0
、と同等であり、tell
with>>
を使用してアイテムをログに追加する方法を示しています。)
runListWriter ex1
GHCiで評価すると、ログに「foo」と「bar」が書き込まれ、結果値が生成されたことがわかります0
。
λ> runListWriter ex1
(["foo","bar"],0)
それでは、これを一般的なライターモナドに変換する方法を見てみましょう。ライターモナドは、リストだけでなく、組み合わせることができるあらゆる種類のもので機能します。具体的には、次のいずれかで機能しますMonoid
。
class Monoid m where
mempty :: m -- an empty m
mappend :: m -> m -> m -- combine two m's into a single m
リストは、それぞれ[]
と(++)
asmempty
とasとのモノイドmappend
です。モノイドのリスト以外の例は、整数の合計です。
λ> Sum 1 <> Sum 2 -- (<>) = mappend
Sum {getSum = 3}
作家のモナドは
newtype Writer w m = Writer { runWriter :: (w, m) }
w
'のリストの代わりに、単一のwがあります。しかし、モナドを定義するときw
は、Monoid
それが空のログから始めてログに新しいエントリを追加できるようにするためです。
instance Monoid w => Monad (Writer w) where
return a = Writer (mempty, a) -- produce an a, don't log anything
Writer (w, a) >>= k =
let Writer (x, a') = k a -- we combine the two w's rather than
in Writer (w <> x, a') -- (++)-ing two lists
ここでの違いに注意してください。のmempty
代わりに[]
、の(<>)
代わりにを使用します(++)
。これが、リストから任意のモノイドに一般化する方法です。
したがって、ライターモナドは、実際には、リストモナドを、単なるリストではなく、組み合わせることができる任意のものに一般化したものです。でリストを使用して、Writer
(ほぼ)同等のものを取得できますListWriter
。唯一の違いは、ログに追加するときに、ログに記録されたアイテムをリストでラップする必要があることです。
ex2 :: Writer [String] Int
ex2 = do
tell ["foo"]
tell ["bar"]
return 0
しかし、同じ結果が得られます。
λ> runWriter ex2
(["foo","bar"],0)
これは、「リストに入れられるアイテム」をログに記録する代わりに、「リスト」をログに記録しているためです。(これは、複数の要素のリストを渡すことで、同時に複数のアイテムをログに記録できることを意味します。)
Writerのリスト以外の使用例については、ソート関数が行う比較をカウントすることを検討してください。関数が比較を行うたびに、次のことができtell (Sum 1)
ます。(誰かに言うことができます。それを取得しますか?これはオンですか?)そして、最後に、ソートされたリストとともに、すべての比較の合計数(つまり、合計)を取得します。
注:これらListWriter
とWriter
定義を自分で使用しようとすると、GHCはインスタンスFunctor
とApplicative
インスタンスが欠落していることを通知します。Monad
インスタンスを取得したら、他のインスタンスをその用語で記述できます。
import Control.Monad (ap, liftM)
instance Functor (ListWriter w) where
fmap = liftM
instance Applicative (ListWriter w) where
pure = return
(<*>) = ap
そして同様にWriter
。わかりやすくするために、上記では省略しました。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加