Skip to content

Commit 812d991

Browse files
author
Mizunashi Mana
committed
Fix by reviews
1 parent a310911 commit 812d991

File tree

1 file changed

+10
-8
lines changed

1 file changed

+10
-8
lines changed

preprocessed-site/posts/2020/io-monad-and-sideeffect.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ headingBackgroundImage: ../../img/background.png
55
headingDivClass: post-heading
66
author: Mizunashi Mana
77
postedBy: Mizunashi Mana
8-
date: April 04, 2020
8+
date: April 05, 2020
99
tags:
1010
...
1111
---
@@ -16,15 +16,15 @@ Haskell は他のプログラミング言語には見られない特徴を多く
1616

1717
## 純粋性とは何か
1818

19-
Haskell は純粋関数型プログラミング言語 (purely functional programming language) を売りにしている。関数型 (functional) の部分は他に任せるとして、ここでは純粋性 (purely) に着目しよう。純粋性とはなんだろうか? どういう条件を満たせば、プログラミング言語は純粋と言えるんだろうか? [Haskell の公式サイト](https://www.haskell.org/) ではこう述べられている:
19+
Haskell は純粋関数型プログラミング言語 (purely functional programming language) を売りにしている。関数型 (functional) の部分は他に任せるとして、ここでは**純粋 (purely)** の部分に着目しよう。純粋とはなんだろうか? どういう条件を満たせば、プログラミング言語は純粋と言えるんだろうか? [Haskell の公式サイト](https://www.haskell.org/) ではこう述べられている:
2020

2121
> Every function in Haskell is a function in the mathematical sense (i.e., "pure").
2222
>
2323
> Haskell の全ての関数は、数学の意味での関数 (つまり「純粋」) です。
2424
>
2525
> -- https://www.haskell.org/
2626
27-
ふむ、数学的な意味での関数とはなんだろうか? 関数が純粋とはどういうことを指すんだろうか? これは噛み砕くと、
27+
ふむ、どうやら全ての関数が、数学的な意味での関数であれば、そのプログラミング言語は純粋と言えるようだ。ところで、数学的な意味での関数とはなんだろうか? 関数が純粋とはどういうことを指すんだろうか? これは噛み砕くと、
2828

2929
1. 関数はどんな時も、同じ引数を与えられたら同じ結果を返す
3030

@@ -95,7 +95,7 @@ putStrLn :: String -> IO ()
9595
* 動作つまり何をするかを表す値を持つ
9696
* その動作をした結果得られる値の型が `a` であることを表す
9797

98-
[^in-other-words]: 動作は計算 (computation) とも呼ばれる
98+
[^in-other-words]: 動作は計算 (computation) とも呼ばれるまた日本の Haskell コミュニティでは英語そのままでアクションとも呼ばれている
9999

100100
型だ抽象的すぎてあまりピンとこないかもしれないもしその動作が結果を返す以外に何もしないならそれは純粋な操作であるから次のように書ける:
101101

@@ -193,7 +193,7 @@ Haskell の `putStrLn` が純粋な理由は分かってもらえただろうか
193193

194194
> 主張は分かったが、純粋に扱うだけに制限するということは、普通のプログラミング言語より非純粋な動作を上手く扱えないんじゃないか
195195
196-
と疑問に思う人もいるだろう。これも当然の疑問だ。普通のプログラミング言語は、表現力豊で、様々な制御構文を持ち、それぞれの構文が純粋性に拘らないため、とてもユニークな非純粋なプログラムを書くことができる。ただ、安心して欲しい。Haskell も、それに負けない表現力で、非純粋な動作を作成することができる。さて、Haskell は、普通のプログラミング言語の機構の基盤は
196+
と疑問に思う人もいるだろう。これも当然の疑問だ。普通のプログラミング言語は、表現力豊かで、様々な制御構文を持ち、それぞれの構文が純粋性に拘らないため、とてもユニークな非純粋なプログラムを書くことができる。ただ、安心して欲しい。Haskell も、それに負けない表現力で、非純粋な動作を作成することができる。さて、Haskell は、普通のプログラミング言語の機構の基盤は
197197

198198
* 2つの動作を上手く結合できること
199199

@@ -362,7 +362,7 @@ ifIO b act1 act2 = case b of
362362

363363
このプログラムは、条件を表す引数と、`IO` 動作を2個受け取り、条件によって2つの動作のうちのどちらかを返していた。これは考えてみれば、とても不思議で強力なことだと思わないだろうか? 普通のプログラミング言語の `if` 文は、条件から書かれたプログラムのどちらかを実行する。一方、`ifIO` は実行を制御しているわけではない。単に、普通の関数と同じように、2つの動作を受け取って、そのうちの片方を関数の返り値として返すだけだ。`ifIO` を呼び出したプログラマは、返ってきた動作をゴミ箱に捨ててもいいし、`(>>=)` で繋げて「2回続けて同じ動作をする」1つの動作にしてもいい。もちろんその動作も `main` に組み入れるかはプログラマ次第だ。なんなら、`main` 以外にライブラリの一部としてグローバルに定義してもいい。ライブラリを使うユーザは、やっぱりそれを使うも使わないも自由だ。`main` に組み入れない限り、その動作は単なるデータであり、実行もされない。
364364

365-
`IO` 動作がデータであることは、プログラムをより豊にする。さっきの `ifIO` は、条件によって片方の動作を返していた。`IO`動作はもっと多彩に制御できる。例えば、条件によって動作の順番を変えたかったら次のように書けばいい:
365+
`IO` 動作がデータであることは、プログラムをより豊かにする。さっきの `ifIO` は、条件によって片方の動作を返していた。`IO`動作はもっと多彩に制御できる。例えば、条件によって動作の順番を変えたかったら次のように書けばいい:
366366

367367
```haskell
368368
chooseOrderIO :: Bool -> IO a -> IO a -> IO a
@@ -439,7 +439,9 @@ newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))
439439
IO $ \s# -> (# s#, \() -> s# #)
440440
```
441441

442-
`s#` を2箇所で使ってるため `IO (() -> State# RealWorld)` の値になれないし、
442+
[^special-syntax-for-unboxed-tuples] `s#` を2箇所で使ってるため `IO (() -> State# RealWorld)` の値になれないし、
443+
444+
[^special-syntax-for-unboxed-tuples]: `(# x, y #)``(# a, b #)` 型の値を表す特別な構文だ。ここでは詳細は述べないので、`x``y` のタプルの特別な表記方法だと思ってもらって構わない。
443445

444446
```haskell
445447
IO $ \s# -> (# s#, IO $ \_ -> (# s#, () #) #)
@@ -597,6 +599,6 @@ main = IO $ \r0# ->
597599

598600
* `State# RealWorld` の型の値は、必ず1回だけ使用される
599601

600-
という契約も意義が見えてくる。もし、この契約が破られると、途中でデータ依存が分岐したり、または宙に消えたりすることになる。そうなると、動作がどういう挙動をするかは、Haskell 内では規定されなくなってしまう。実際に、最適化によってどう動作するかが変わってきてしまう例も作れる。`IO` の契約とは、データ依存が必ず一本の線で繋がり、Haskell の評価の枠できちんと順番が規定されるということを保証しているのだ。
602+
という契約も意義が見えてくる。もし、この契約が破られると、途中でデータ依存が分岐したり、または途中で途絶えたりすることになる。そうなると、動作がどういう挙動をするかは、Haskell 内では規定されなくなってしまう。実際に、最適化によってどう動作するかが変わってきてしまう例も作れる。`IO` の契約とは、データ依存が必ず一本の線で繋がり、Haskell の評価の枠できちんと順番が規定されるということを保証しているのだ。
601603

602604
これが、GHC がこのような定義を `IO` で採用している理由になる。もちろん、アナロジーとして現実世界全体を表す架空の状態を `State# RealWorld` と見立て、`IO`動作の実行により新たな現実世界全体の状態が手に入るという見方は可能だ。名前の由来もそこから来ている。ただ、基本的には、GHC において、特別な仕組みを入れずに `IO` を実装するためのやり方であるということを押さえておいて欲しい。

0 commit comments

Comments
 (0)