[解決済み] Haskellで副作用がモナドとしてモデル化されているのはなぜですか?
質問
Haskellの不純物計算がなぜモナドとしてモデル化されているのか、どなたかご指摘ください。
モナドは4つの演算を持つ単なるインタフェースですが、そこに副作用をモデル化する理由は何だったのでしょうか?
解決方法は?
ある関数に副作用があるとします。その関数が生み出す効果をすべて入出力パラメータとするならば、その関数は外界に対して純粋である。
では、不純物の多い関数の場合
f' :: Int -> Int
現実世界を考察に加える
f :: Int -> RealWorld -> (Int, RealWorld)
-- input some states of the whole world,
-- modify the whole world because of the side effects,
-- then return the new world.
では
f
はまた純粋なものです。パラメタライズされたデータ型
type IO a = RealWorld -> (a, RealWorld)
そのため、RealWorldと何度もタイプする必要はなく、次のように書けばよい。
f :: Int -> IO Int
プログラマにとって、RealWorldを直接扱うことは危険すぎる。特に、プログラマがRealWorld型の値を手に入れた場合、次のようなことが起こりうる。 コピー これは基本的に不可能です。 (例えば、ファイルシステム全体をコピーしようとすることを考えてみてください。 どこに置くのでしょうか?) したがって、IOの定義は全世界の状態をもカプセル化するのです。
不純物関数の合成
これらの不純物関数は、連鎖させることができなければ意味がない。を考えてみましょう。
getLine :: IO String ~ RealWorld -> (String, RealWorld)
getContents :: String -> IO String ~ String -> RealWorld -> (String, RealWorld)
putStrLn :: String -> IO () ~ String -> RealWorld -> ((), RealWorld)
にしたいのです。
- 得る コンソールからファイル名を取得します。
- 読む そのファイルを
- プリント ファイルの内容をコンソールに表示します。
実世界の状態にアクセスできるとしたら、どうすればいいのでしょうか。
printFile :: RealWorld -> ((), RealWorld)
printFile world0 = let (filename, world1) = getLine world0
(contents, world2) = (getContents filename) world1
in (putStrLn contents) world2 -- results in ((), world3)
ここでパターンが見えてきました。関数はこのように呼び出されます。
...
(<result-of-f>, worldY) = f worldX
(<result-of-g>, worldZ) = g <result-of-f> worldY
...
そこで、演算子を定義することができます。
~~~
を使用して結合します。
(~~~) :: (IO b) -> (b -> IO c) -> IO c
(~~~) :: (RealWorld -> (b, RealWorld))
-> (b -> RealWorld -> (c, RealWorld))
-> (RealWorld -> (c, RealWorld))
(f ~~~ g) worldX = let (resF, worldY) = f worldX
in g resF worldY
であれば、単純に次のように書けばよい。
printFile = getLine ~~~ getContents ~~~ putStrLn
現実の世界に触れることなく
"不純物"
ここで、ファイルの内容も大文字にしたいとします。大文字にするのは純粋な関数です
upperCase :: String -> String
しかし、現実の世界でそれを実現するためには、それは
IO String
. このような関数を持ち上げるのは簡単です。
impureUpperCase :: String -> RealWorld -> (String, RealWorld)
impureUpperCase str world = (upperCase str, world)
これは一般化できる。
impurify :: a -> IO a
impurify :: a -> RealWorld -> (a, RealWorld)
impurify a world = (a, world)
ということで
impureUpperCase = impurify . upperCase
と書くことができます。
printUpperCaseFile =
getLine ~~~ getContents ~~~ (impurify . upperCase) ~~~ putStrLn
<サブ
(注) 通常は
getLine ~~~ getContents ~~~ (putStrLn . upperCase)
)
私たちはずっとモナドを使って仕事をしていた
さて、何ができたか見てみましょう。
-
演算子を定義しました。
(~~~) :: IO b -> (b -> IO c) -> IO c
これは、2つの不純物関数を連結する -
私たちは関数
impurify :: a -> IO a
純粋な値を不純物に変換する。
ここで、識別を行う
(>>=) = (~~~)
と
return = impurify
で、ほら。モナドの出来上がりです。
テクニカルノート
モナドであることを確認するために、まだいくつかの公理もチェックする必要があります。
-
return a >>= f = f a
impurify a = (\world -> (a, world)) (impurify a ~~~ f) worldX = let (resF, worldY) = (\world -> (a, world )) worldX in f resF worldY = let (resF, worldY) = (a, worldX) in f resF worldY = f a worldX
-
f >>= return = f
(f ~~~ impurify) worldX = let (resF, worldY) = f worldX in impurify resF worldY = let (resF, worldY) = f worldX in (resF, worldY) = f worldX
-
f >>= (\x -> g x >>= h) = (f >>= g) >>= h
エクササイズとして残す。
関連
-
[解決済み] Haskell Preludeの'const'は何のためにあるのか?
-
[解決済み] Haskellは実世界で何に使われているのか?[クローズド]
-
[解決済み] フリーモナドとは何ですか?
-
[解決済み】なぜモナドが必要なのか?
-
[解決済み] Haskellにはなぜ "data "と "newtype "があるのですか?重複] [重複] [重複
-
[解決済み] 制約条件付き特殊化
-
[解決済み] Haskellにおける "リフティング "とは?
-
[解決済み] TLSサーバーを実装するためのHsOpenSSL APIの適切な使用法
-
[解決済み] Haskellの "Just "構文とは?
-
[解決済み] ハスケル Where vs. Let
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】Haskellでの挿入ソート
-
[解決済み] haskellにおけるdrop関数 - リスト内包を用いた実装
-
[解決済み] 解釈の仕方 (Eq a)
-
[解決済み] .の違いは何ですか?(ドット)と$(ドルマーク)の違いは何ですか?
-
[解決済み] Haskellにはなぜ "data "と "newtype "があるのですか?重複] [重複] [重複
-
[解決済み] Haskell における `mod` と `rem` の違い
-
[解決済み] GHCiの複数行コマンド
-
[解決済み] GHCはなぜこんなに大きいのか/大きいのか?
-
[解決済み] Haskellでグラフはどのように表現するのか?
-
[解決済み] GHCでコンパイルした小さなHaskellプログラムを巨大なバイナリにする