8
8
9
9
従来、` IO ` (命令)の結果に対して(` a -> IO b ` というような型ではなく、` a -> b ` という型の)普通の関数を実行するには、次のように` do ` の中で左細い矢印` <- ` を使って、一旦変数に代入する必要がありました。
10
10
11
- ```
11
+ ``` haskell
12
12
do
13
13
line <- getLine
14
14
15
15
-- line 変数に代入して、length関数を実行してから何かする
16
16
print (length line)
17
+
17
18
-- あるいは、length関数を実行してから何もせずにそのまま返す
18
19
return (length line)
19
20
```
20
21
21
- 課題19で紹介した ` >>= ` を使うことによって、` do ` や代入する変数を用意する必要はなくなりましたが、` return ` などの関数を使って、必ず何らかの形で ` IO ` (命令)に変換する必要がありました。
22
+ 課題hoge(19?)で紹介した ` >>= ` を使うことによって、` do ` や代入する変数を用意する必要はなくなりましたが、` return ` などの関数を使い、必ず何らかの形で右辺の関数を、 ` IO ` (命令)を返す関数に変換する必要がありました:
22
23
23
- ```
24
+ ``` haskell
24
25
getLine >>= return . length
25
26
```
26
27
27
- この変換を不要にするのが` fmap ` 、あるいはその演算子バージョンである` <$> ` の役目です(実際のところ` <$> ` の方がよく使われるので、ここからは` <$> ` で説明します)。
28
- 上記の例を書き換えてみると、こう👇なります。
28
+ この変換を不要にするのが` fmap ` 、あるいはその演算子バージョンである` <$> ` の役目です(実際のところ` <$> ` の方がよく使われるので、ここからは` <$> ` で説明します)。
29
29
30
- ```
30
+ 上記の例を` <$> ` を使って書き換えてみると、こう👇なります:
31
+
32
+ ``` haskell
31
33
length <$> getLine
32
34
```
33
35
34
- 順番が変わってしまっている点には、ご注意ください。
35
- ` >>= return . ` という部分が` <$> ` に置き換わって、すっきりしましたね!
36
+ ` >== ` から両辺に渡す式の型が入れ替わっている点には、ご注意ください。` <$> ` では、左辺に「普通の関数」、右辺に「命令」を渡します。` >>= return . ` という部分が` <$> ` に置き換わって、すっきりしましたね!
36
37
37
- このように` <$> ` は、** ` IO ` (命令)を実行した結果に対して 、普通の、純粋な関数を実行する ** 命令を作るための演算子です 。
38
+ このように` <$> ` は、** ` IO ` (命令)の結果 ** に対して 、普通の、** 純粋な関数 ** を実行する命令を作るための演算子です 。
38
39
39
40
そのことを裏付けるために、例のごとく` :t ` で型定義を覗いてみましょう。
40
41
41
- ```
42
+ ``` haskell
42
43
ghci> : t (<$>)
43
44
(<$>) :: Functor f => (a -> b ) -> f a -> f b
44
45
```
45
46
46
- おっと、` Monad ` でもない、` Functor ` という聞き慣れない型クラスの型変数が出てきました。
47
- ` Functor ` の説明は一旦置いておいて、前の課題と同様` TypeApplications ` を使って型変数` f ` を` IO ` に置き換えて解釈しましょう。
47
+ おっと、 `Monad `でもない、 `Functor `という聞き慣れない型クラスの型変数が出てきました。 `Functor `の説明は一旦置いておいて、 前の課題と同様`TypeApplications `を使って型変数`f` を`IO `に置き換えて解釈しましょう。
48
48
49
- ```
49
+ ```haskell
50
50
ghci> : set - XTypeApplications
51
51
ghci> : t (<$>) @ IO
52
52
(<$>) @ IO :: (a -> b ) -> IO a -> IO b
53
53
```
54
54
55
- 2つの引数と戻り値 :
55
+ 上記のように ` <$> ` の型変数 ` f ` を ` IO ` に絞ったバージョンを作ると、2つの引数と戻り値は次のような型となります :
56
56
57
57
- 第1引数(左辺)の` a -> b ` : 普通の(入出力を行わない)関数 ` a -> b ` 。
58
58
- 第2引数(右辺)の` IO a ` : 第1引数の関数の引数と、同じ型の値を返す` IO ` (命令)。
59
59
- 戻り値の` IO b ` : 第1引数(左辺)の関数と、同じ型の値を返す命令。
60
60
61
- ` IO a ` を` a -> b ` で` IO b ` に変換する演算子である、というのが伝わるでしょうか?
62
- ` length <$> getLine ` の例に当てはめると、` getLine ` で「ユーザーから入力してもらった文字列」に対して、` length ` 関数を実行して得た文字列の長さを返す` IO ` (命令)を作っていますね!
61
+ ` IO a ` を` a -> b ` で` IO b ` に変換する演算子である、というのが伝わるでしょうか?` length <$> getLine ` の例に当てはめると、` getLine ` で「ユーザーから入力してもらった文字列」に対して、` length ` 関数を実行して得た文字列の長さを返す` IO ` (命令)を作っていますね!
63
62
64
- ```
63
+ ``` haskell
65
64
length <$> getLine
66
65
^^^^^^ ^^^^^^^
67
66
[Char ] -> Int IO [Char ]
@@ -76,25 +75,20 @@ ghci> :t (<$>) @IO
76
75
f b
77
76
```
78
77
79
- ここで重要なのは、第1引数である関数はあくまでも` a -> b ` という型の、「普通の関数」として** 扱われる** 、という点です。
80
- ` <$> ` を` IO ` を返す関数` a -> IO b ` に対して使うと、** 型エラーにはならず** 、妙な動作になってしまう場合があるのでご注意ください。
78
+ ただし厳密に言えば、第1引数である関数はあくまでも` a -> b ` という型の、「普通の関数」として** 扱われる** 、という点にはご注意ください。` <$> ` を` IO ` を返す関数` a -> IO b ` に対して使うと、** 型エラーにはならず** 、妙な動作になってしまう場合があります。例えば、` putStrLn <$> getLine ` とGHCi上で書いた場合...
81
79
82
- 例えば、` putStrLn <$> getLine ` とGHCi上で書いた場合...
83
-
84
- ```
80
+ ``` haskell
85
81
ghci> putStrLn <$> getLine
86
82
aaaaa -- ここはユーザーによる入力
87
83
```
88
84
89
- ` putStrLn =<< getLine ` と書いたときと異なり、入力した文字列が` putStrLn ` によって出力されなかったことにお気づきでしょうか?
90
- そう、` getLine ` が実行されてユーザーから入力を得たものの、なぜかそれを受け取ったはずの` putStrLn ` は実行されなかったのです...😱
85
+ ` putStrLn =<< getLine ` と書いたときと異なり、入力した文字列が` putStrLn ` によって出力されなかったことにお気づきでしょうか?そう、` getLine ` が実行されてユーザーから入力を得たものの、なぜかそれを受け取ったはずの` putStrLn ` は実行されなかったのです...😱
91
86
92
87
#### なぜ` putStrLn <$> getLine ` と書いても` putStrLn ` は実行されないのか、あるいは型注釈を付けようという話
93
88
94
- この理由については、難しい話になるので節を分けます。
95
- 今回の課題を解くだけの目的では必要ないばかりか、あなたの今後のHaskeller人生で絶対に必要な知識でもないので、わからなかったら適当に飛ばしてください。
89
+ この理由については難しい話になるので節を分けます。今回の課題を解くだけの目的では必要ないばかりか、あなたの今後のHaskeller人生で絶対に必要な知識でもないので、わからなかったら適当に飛ばしてください。
96
90
97
- ```
91
+ ``` haskell
98
92
ghci> : t putStrLn <$> getLine
99
93
putStrLn <$> getLine :: IO (IO () )
100
94
```
@@ -108,32 +102,25 @@ putStrLn <$> getLine :: IO (IO ())
108
102
- ` IO ` に対する` <$> ` は` (a -> b) -> IO a -> IO b ` という型なので、
109
103
- 型変数` a ` に` [Char] ` 、型変数` b ` に` IO () ` が代入された。
110
104
111
- そう、` IO () ` も、実態は単なる普通の型なので、普通に型推論され、普通に型変数に代入されます。
112
- これまで扱ってきた` Integer ` や` Bool ` 、` [a] ` などと変わらない、普通の型なのです。
105
+ そう、` IO () ` 、実態は単なる普通の型なので、普通に型推論され、普通に型変数に代入されます。これまで扱ってきた` Integer ` や` Bool ` 、` [a] ` などと変わらない、普通の型なのです。
113
106
114
- 型と言うことは何らかの値を表しているはずです。これまではっきりと説明してきませんでしたが、一体` IO ` 型の値は何を表しているのでしょう?
115
- ` IO ` は「命令」を表すオブジェクトです。
107
+ 型と言うことは何らかの値を表しているはずです。これまではっきりと説明してきませんでしたが、一体` IO ` 型の値は何を表しているのでしょう?
116
108
117
- 例えるなら、ほかのプログラミング言語で言うところの「関数オブジェクト」に近いです。
118
- PythonやJavaScriptにおける関数オブジェクト、Rubyで言えば` Proc ` クラスのオブジェクト、Javaにおける` Callable ` インターフェースを実装したオブジェクトなどなど
109
+ ` IO ` は「命令」を表すオブジェクトです。例えるなら、ほかのプログラミング言語で言うところの「関数オブジェクト」に近いです。PythonやJavaScriptにおける関数オブジェクト、Rubyで言えば` Proc ` クラスのオブジェクト、Javaにおける` Callable ` インターフェースを実装したオブジェクトなどなど。
119
110
120
- ただし、ほかのプログラミング言語で言うところの、「関数オブジェクト」と異なり、引数を受け取りません。
121
- Haskellでは引数を受け取るのは、あくまでも普通の関数` a -> b ` の役目なのです。
122
- ` putStrLn ` も引数に当たる` [Char] ` は` [Char] -> IO () ` という型の「普通の関数」の引数として受け取っていますね。
111
+ ただし、ほかのプログラミング言語で言うところの、「関数オブジェクト」と異なり、引数を受け取りません。Haskellでは引数を受け取るのは、あくまでも普通の関数` a -> b ` の役目なのです。` putStrLn ` も引数に当たる` [Char] ` は` [Char] -> IO () ` という型の「普通の関数」の引数として受け取っています。
123
112
124
- 一方、普通の関数` a -> b ` は、` IO ` のように入出力処理が出来ません。
125
- なので入出力処理の部分は` IO ` に任せているのです。
113
+ 一方、普通の関数` a -> b ` は、` IO ` のように入出力処理が出来ません。なので入出力処理の部分は` IO ` に任せているのです。
126
114
127
- 結果、ほかのプログラミング言語における「関数オブジェクト」は、自由に入出力ができる、関数の戻り値(や引数)になることができる、という点で、Haskellの` IO ` と似ています。
128
- 従って、「` IO ` は引数を受け取らない関数オブジェクト」というイメージで捉えてください。
115
+ したがって、ほかのプログラミング言語における「関数オブジェクト」は、「自由に入出力ができる」、「関数の戻り値(や引数)になることができる」、という点で、Haskellの` IO ` と似ています。「` IO ` は引数を受け取らない関数オブジェクト」と捉えてください。
129
116
130
117
以上の通り、「関数オブジェクト」とよく似たこの` IO ` は単なる値として扱えるので、Haskellにおける関数` a -> b ` の結果` b ` として返したり、` putStrLn <$> getLine ` が返す` IO (IO ()) ` のように、` IO ` を実行した結果として返すことができます。
131
118
132
- では、その結果として返された` IO ` はどうやって実行することができるのでしょうか?
133
- ` main ` 関数やGHCiの中で評価されて(` >>= ` や` >> ` でつなげられてその部分を通ったとき)初めて実行されます。
134
- ほかのプログラミング言語の「関数オブジェクト」と異なり、実行するための演算子が明確でなく、わかりづらい
119
+ では肝心な点、その結果として返された` IO ` はどうやって実行することができるのでしょうか?簡潔に言うと、` main ` 関数やGHCiの中で評価することで(` >>= ` や` >> ` でつなげられてその部分を通ったとき)初めて実行できます。ほかのプログラミング言語における「関数オブジェクト」と異なり、実行するための演算子が明確でなく、わかりづらくなっています。
135
120
136
- ```
121
+ 先程の` putStrLn <$> getLine ` を、今度こそ全部実行してみましょう:
122
+
123
+ ``` haskell
137
124
ghci> : t putStrLn <$> getLine
138
125
putStrLn <$> getLine :: IO (IO () )
139
126
ghci> returnedAction <- putStrLn <$> getLine
0 commit comments