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でのコメントに心から感謝します。



All Articles