3つの便利なモナド

注意:以下のテキストを読む前に、あなたはすでにモナドが何であるかを知っているはずです。 そうでない場合は、まずこの投稿を読んでください!

half機能は次のとおりです。


そして、それを数回適用できます:
 half . half $ 8 => 2 


すべてが期待どおりに機能します。 しかし、あなたは、この関数で何が起こっているかをログに記録しておくといいと思いました:


 half x = (x `div` 2, "    " ++ (show x) ++ "!") 


まあ、素晴らしい。 しかし、今度はhalf数回適用したい場合はどうなりますか?
 half . half $ 8 


これ実現したいことです。


ネタバレ :これは自動的には行われません。 すべてをペンでペイントする必要があります。
 finalValue = (val2, log1 ++ log2) where (val1, log1) = half 8 (val2, log2) = half val1 


ふう! これは簡潔なものではありません
 half . half $ 8 


しかし、ログを持つ関数がさらにある場合はどうでしょうか? これは、次のスキームを要求します。値を含むログを返す関数ごとに、これらのログを結合します。 これは副作用であり、モナドのような副作用に強い人はいません!

モナドライター



作家のモナドが白い馬に登場
Monad Writerはクールな性格です。 「心配しないで、ログを処理します」と彼女は言います。 「クリーンなコードに戻り、Zeppelinを最大限に活用してください!」

Writerの各インスタンスにはログと戻り値があります。


 data Writer wa = Writer { runWriter :: (a, w) } 


Writerを使用すると、次のようなコードを記述できます
 half 8 >>= half 


さて、または、モナドの関数の構成である<=<関数を使用して<=<を取得できます。
 half <=< half $ 8 

half . half $ 8近いhalf . half $ 8 half . half $ 8 。 かっこいい!

tellを使用して、ログに何かを書き込むことができます。 そして、 return値はこの値をWriterに渡します。 更新されたhalf関数は次のとおりです。
 half :: Int -> Writer String Int half x = do tell ("I just halved " ++ (show x) ++ "!") return (x `div` 2) 


彼女はWriterを返します。


また、 runWriterを使用して、 runWriterから値を抽出できます。


 runWriter $ half 8 => (4, "I just halved 8!") 


しかし、最もクールな点は、 >>=を使用してhalfコールをチェーンできることです。
 runWriter $ half 8 >>= half => (2, "I just halved 8!I just halved 4!") 


発生することは次のとおりです。


魔法のように>>= 2つのライターを組み合わせる方法を知っているので、面倒なコードを自分で書く必要はありません! 完全な定義は次のとおりです>>=


以前にまったく同じテンプレートコードを作成しましたが、今では>>=がこの責任を引き受けました。 かっこいい! また、値を取得してモナドに入れるreturn関数も使用します。


 return val = Writer (val, "") 


:この定義はほぼ正しい。実際、 Writerモナドでは、行だけでなく、 Monoidをログとして使用できます。ここで少し簡略化しました。)

Moner Writerありがとう!

モナドリーダー


いくつかの設定を多くの機能に渡したいとします。 モナドリーダーを使用するだけです。


これにより、すべての機能に意味を伝えることができ、伝送メカニズム自体を背後で隠します。 例:
 greeter :: Reader String String greeter = do name <- ask return ("hello, " ++ name ++ "!") 


greeterはモナドリーダーを返します。


彼女の定義は次のとおりです。
 data Reader ra = Reader { runReader :: r -> a } 


リーダーは常に脱走者でした。 ダークホース。 Readerは、関数を処理することができるため、それを考慮すると混乱するため、異なります。 しかし、 runReaderを使用してこの関数を抽出できることは誰もが理解しています。


そして、この関数にいくつかの関数を渡すことができます。この関数は、 greeter使用されます。


 runReader greeter $ "adit" => "hello, adit!" 


そのようにして、 >>=を使用すると、リーダーが返されます。 状態を渡すと、このモナドに含まれるすべての関数に状態が転送されます。


 m >>= k = Reader $ \r -> runReader (k (runReader mr)) r 


リーダーは常にやや複雑でした。 これは一般的に最高の品質です。
returnは、値をReader入れます。


 return a = Reader $ \_ -> a 


最後に、 askは返された状態を返します。
 ask = Reader $ \x -> x 


Readerでもっと時間を過ごしたいですか? パンクロックをカットして、より長い例を見てみましょう。

モナド州


State Monadは、読者のより印象的な親友です。


Readerモナドとまったく同じですが、その助けを借りて、読むだけでなく書くこともできます!
Stateの定義は次のとおりです。
 State sa = State { runState :: s -> (a, s) } 




getで状態をgetか、 put状態を変更できます。 以下に例を示します。
 greeter :: State String String greeter = do name <- get put "tintin" return ("hello, " ++ name ++ "!") runState greeter $ "adit" => ("hello, adit!", "tintin") 


いいね Readerが「you-me-not-change」のキャラクターを持っている場合、Stateは、反対に、関係を求め、自分自身を変更する準備ができています。
Stateモナドの定義は、モナドReader定義に非常に似ています:
return


 return a = State $ \s -> (a, s) 


>>=


 m >>= k = State $ \s -> let (a, s') = runState ms in runState (ka) s' 


おわりに




作家 読者 州。 今日、Haskellの兵器庫に3つの強力な武器を追加しました。 それらを賢く使用してください。

翻訳者から:この翻訳の品質を改善するのに役立つPMでのコメントに心から感謝します。

Source: https://habr.com/ru/post/J184722/


All Articles