モナドについて
3つ質問があります。
- モナドは、引数を取らない関数のような物で包み込まれて
評価 → その包がとれる = 入出力など行わないので参照透過
評価して実行? → 包がとれる → 入出力などの副作用ある関数が評価される。
みたいな認識で合ってますか? - FunctorとApplicativeとMonadの違い
- 何故モナドを使うのか?
高校生なので、圏論がわかりません。なので、出来れば他の言語で擬似的に実装してくれるとありがたいです。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答1件
0
ベストアンサー
まず、モナドを抽象的なまま理解しようとするのは難しいと思います。具体的なモナドをいくつか理解した上でそれら共通点を見出す方が簡単かと思います。
また、Haskell を使う上で圏論を理解する必要はありません。
モナドは、引数を取らない関数のような物で包み込まれて
評価 → その包がとれる = 入出力など行わないので参照透過
評価して実行? → 包がとれる → 入出力などの副作用ある関数が評価される。
みたいな認識で合ってますか?
質問の表現が理解しきれていませんが、入出力という単語があるのでおそらく IO モナドを念頭に置いているのだろうという想像で回答します。
例えば putStrLn "hi"
は IO ()
型の値で「手続き」を表します、これを評価しても「手続き」以上の意味はなく、標準出力に「hi」が出力されるわけではありません。ここで seq
は seq a b
の a
を先に評価する特殊な関数です。(Wandbox で実行)
haskell
1main :: IO () 2main = seq (putStrLn "hi") $ pure ()
JavaScrpit でムリヤリ実装すると次のような感じでしょうか。
javascript
1class Unit {} 2unit = new Unit(); 3 4hi = function () { console.log("hi"); return unit; } 5 6hi
「手続き」を実行するためには main
に埋め込む必要があります。(unsafePerformIO
というものもありますが、ここでは説明しません。)(Wandbox で実行)
haskell
1main :: IO () 2main = putStrLn "hi"
javascript
1class Unit {} 2unit = new Unit(); 3 4hi = function () { console.log("hi"); return unit; } 5 6main = hi // main は処理系によって特別扱いされる変数と仮定します
「手続き」は単なる値で、main
が特殊扱いされて実行されることに注意してください。
FunctorとApplicativeとMonadの違い
これらを重点的に説明したのが『すごい Haskell たのしく学ぼう!』です。この書籍を読むことをおすすめします。ここではリストモナドに限って説明します。その書籍の半分ほどの分量をそれら説明が占めているので、全部説明しようとするとその量になってしまいますから。
ファンクターであれば次のことができるようになります。(>
は REPL のプロンプトと見てください。)
haskell
1> (* 2) <$> [1, 2, 3] 2[2, 4, 6]
javascript
1function map (f, l) { return l.map(f); } 2 3map(x => x * 2, [1, 2, 3]);
アプリカティブファンクターであれば、上記に追加して次のことができるようになります。
haskell
1> [(* 2), (+ 1)] <*> [1, 2, 3] 2[2, 4, 6, 2, 3, 4]
javascript
1function flat (l) { return l.reduce((a, v) => a.concat(v), []); } 2function apply (fs, l) { return flat(fs.map(f => l.map(f))); } 3 4apply([x => x * 2, x => x + 1], [1, 2, 3]);
モナドであれば、上記に追加して次のことができるようになります。
haskell
1> [1, 2, 3] >>= \x -> ['a' : show x, show x <> "b"] >>= \y -> pure (x, y) 2[(1, "a1"), (1, "1b"), (2, "a2"), (2, "2b"), (3, "a3"), (3, "3b")]
javascript
1function flat (l) { return l.reduce((a, v) => a.concat(v), []); } 2function bind (a, b) { return flat(a.map(e => b(e))); } 3function pure (a) { return [a]; } 4 5bind([1, 2, 3], x => bind(['a' + x, x + 'b'], y => pure([x, y])));
これには do 記法が用意されているため、次のように書くこともできます。どちらでも同じ意味です。
haskell
1> do { x <- [1, 2, 3]; y <- ['a' : show x, show x <> "b"]; pure (x, y) }
何故モナドを使うのか?
モナドを明示的に扱っていない言語でも、実はいろいろなモナドが使われています。
例えば次のようなものがあります。
- リスト内包表記
async
/await
?.
演算子- 例外
多くの言語ではこれらに別々の記法を与えて扱っていますが、モナドに対する記法を1つサポートするだけでこれらは全て扱えます。
そういった意味で言語が簡単になります。
投稿2018/09/27 04:35
編集2018/09/27 06:14総合スコア285
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/09/27 14:05