@@ -116,52 +116,73 @@ putStrLn <$> getLine :: IO (IO ())
116
116
117
117
以上の通り、「関数オブジェクト」とよく似たこの` IO ` は単なる値として扱えるので、Haskellにおける関数` a -> b ` の結果` b ` として返したり、` putStrLn <$> getLine ` が返す` IO (IO ()) ` のように、` IO ` を実行した結果として返すことができます。
118
118
119
- では肝心な点、その結果として返された` IO ` はどうやって実行することができるのでしょうか?簡潔に言うと、` main ` 関数やGHCiの中で評価することで(` >>= ` や` >> ` でつなげられてその部分を通ったとき )初めて実行できます。ほかのプログラミング言語における「関数オブジェクト」と異なり、実行するための演算子が明確でなく、わかりづらくなっています 。
119
+ では肝心な点、その結果として返された` IO ` はどうやって実行することができるのでしょうか?簡潔に言うと、` main ` 関数やGHCiの中で評価することで(` >>= ` や` >> ` 、あるいは ` do ` でつなげられて、その部分を通ったとき )初めて実行できます。ほかのプログラミング言語における「関数オブジェクト」と異なり、実行する専用の演算子 [ ^ execute-operator ] はありません。ちょっと分かりづらいですね 。
120
120
121
- 先程の` putStrLn <$> getLine ` を、今度こそ全部実行してみましょう:
121
+ [ ^ execute-operator ] : JavaScriptやPythonの関数オブジェクトでは` () ` 、Rubyの` Proc ` クラスのインスタンスやJavaの` Callable ` を実装したオブジェクトでは` call ` メソッドがその「実行する専用の演算子」に該当します。
122
+
123
+ 例として、先程の` putStrLn <$> getLine ` を今度こそ全部実行してみましょう:
122
124
123
125
``` haskell
124
- ghci> : t putStrLn <$> getLine
125
- putStrLn <$> getLine :: IO (IO () )
126
- ghci> returnedAction <- putStrLn <$> getLine
127
- 12345 -- ここはユーザーによる入力
128
- ghci> : t returnedAction
129
- returnedAction :: IO ()
130
- ghci> returnedAction
131
- 12345
126
+ ghci> action1 = putStrLn <$> getLine
127
+ ghci> action1 -- 復習: この時点では putStrLn は実行されない
128
+ aaaaa -- ここはユーザーによる入力
129
+ ghci>
130
+ ghci> : t action1 -- IO (IO ()) という型なので、内側の IO () が実行されるまで、putStrLn は実行されない
131
+ action1 :: IO (IO () )
132
+
133
+ -- do の中で<- を使って action1 の中の IO () を取り出して、実行する
134
+ ghci> : {
135
+ ghci| do
136
+ ghci| action2 <- action1
137
+ ghci| action2
138
+ ghci| : }
139
+ aaaaa -- ここはユーザーによる入力
140
+ aaaaa -- 入力した文字列が出力された!
132
141
```
133
142
134
- GHCiは、` putStrLn <$> getLine ` という式を受け取ったとき、とりあず` IO (IO ()) ` の外側の` IO (...) ` 、つまりこの場合` getLine ` に当たる「命令」を実行します。
135
- そして、その結果として ` putStrLn <getLineが返した文字列> ` という ` IO () ` 型の値(命令)を返しているのです。
136
- そのため、上記のように` putStrLn <$> getLine ` から` <- ` で(あるいは、` >>= ` で)取り出した` IO () ` (上記で言うところの` returnedAction ` )を直接GHCiに入力すれば 、` putStrLn <getLineが返した文字列> ` の部分も実行することができます 。
143
+ GHCiは、` putStrLn <$> getLine ` という式を受け取ったとき、とりあず` IO (IO ()) ` の外側の` IO (...) ` 、つまりこの場合` getLine ` に当たる「命令」を実行します。そして、その結果として ` putStrLn <getLineが返した文字列> ` という ` IO () ` 型の値(命令)を返しているのです。
144
+
145
+ そのため、上記のように` putStrLn <$> getLine ` から` <- ` で(あるいは、` >>= ` で)取り出した` IO () ` (上記で言うところの` action2 ` )を直接 ` do ` の中に含めてGHCiに入力することで初めて 、` putStrLn <getLineが返した文字列> ` の部分も実行することができるようになるのです 。
137
146
138
147
` putStrLn <$> getLine ` と書いても` putStrLn ` の部分が実行されなかったのは、` getLine ` が結果として返した` [Char] ` を` putStrLn ` で` IO () ` に変換した後、実行していなかったからなのです。
139
148
140
- この、 ` putStrLn <$> getLine ` と入力しても ` putStrLn ` が実行されない、という現象は、GHCiだけでなく、 ` main ` 関数に ` putStrLn <$> getLine ` と書いてしまった場合も同様に発生します。
149
+ 先程の ` action2 ` を最終的に実行する ` do ` の型を ` :t ` を使って見てみると、ちゃんと入れ子じゃない、普通の ` IO () ` になっていることが確認できます:
141
150
151
+ ``` haskell
152
+ ghci> : {
153
+ ghci| : t do
154
+ ghci| action2 <- action1
155
+ ghci| action2
156
+ ghci| : }
157
+ do
158
+ action2 <- action1
159
+ action2 :: IO ()
142
160
```
161
+
162
+ なお、 この`putStrLn <$> getLine `と入力しても`putStrLn` が実行されない、 という現象は、 GHCi だけでなく、 `main` 関数に`putStrLn <$> getLine `と書いてしまった場合も同様に発生します。
163
+
164
+ ```haskell
143
165
main = putStrLn <$> getLine
144
166
```
145
167
146
168
というファイルを` not-echoed.hs ` という名前で保存して実行してみても、やはりGHCiで実行した場合と同様に、` getLine ` に当たる部分しか実行されません。
147
169
148
- ```
170
+ ``` haskell
149
171
shell> stack exec runhaskell not - echoed. hs
150
172
12345 # ここはユーザーによる入力
151
173
shell>
152
174
```
153
175
154
- しかし、多くのHaskellerが行うように、この問題は` main ` 関数に型注釈をつけていれば、型エラーとして回避できます。
155
- ` not-echoed.hs ` を次のように書き換えてみましょう。
176
+ しかし、多くのHaskellerが行うように、この問題は` main ` 関数に型注釈をつけていれば、型エラーとして回避できます。` not-echoed.hs ` を次のように書き換えてみましょう。
156
177
157
- ```
178
+ ``` haskell
158
179
main :: IO ()
159
180
main = putStrLn <$> getLine
160
181
```
161
182
162
- 今度は以下のような型エラーが報告されるので、問題のある` putStrLn <$> getLine ` は実行されるまでもありません 。
183
+ 今度は以下のような型エラーが報告されるので、問題のある` putStrLn <$> getLine ` は実行される間でもありません 。
163
184
164
- ```
185
+ ``` haskell
165
186
shell> stack exec runhaskell not - echoed. hs
166
187
not - echoed. hs: 2 : 8 : error :
167
188
? Couldn't match type ‘ IO () ’ with ‘ () ’
@@ -174,58 +195,56 @@ not-echoed.hs:2:8: error:
174
195
| ^^^^^^^^^^^^^^^^^^^^
175
196
```
176
197
177
- ` main ` 関数のように、` let ` や` where ` を伴わないで定義された、同じモジュール内のどの関数からも参照できる関数を、「トップレベルに定義された関数」と言います。
178
- ` main ` 関数だけでなく、ほかのトップレベルの` IO ` 型の値を返す関数(あるいは` IO ` 型の値そのもの)にも型注釈を付けていれば、この問題は大抵回避できます。
179
- 一般に、** トップレベルに定義する関数は、型注釈を書く** ことでインターフェースを明確にすることが望ましいとされています。
180
- なので、トップレベルの関数に型注釈を書くことは、この問題を回避する以外のメリットもあるので、なるべく書きましょう。
181
- 当入門ではこれまで` main ` の型注釈については省略してきましたが、今後は` main ` についても記載します。
198
+ ` main ` 関数のように、` let ` や` where ` を伴わないで定義された、同じモジュール内のどの関数からも参照できる関数を、「トップレベルに定義された関数」と言います。` main ` 関数だけでなく、ほかのトップレベルの` IO ` 型の値を返す関数(あるいは` IO ` 型の値そのもの)にも型注釈を付けていれば、この問題は大抵回避できます。一般に、** トップレベルに定義する関数は、型注釈を書く** ことでインターフェースを明確にすることが望ましいとされています。なので、トップレベルの関数に型注釈を書くことは、この問題を回避する以外のメリットもあるので、なるべく書きましょう。当入門ではこれまで` main ` の型注釈については省略してきましたが、今後は` main ` についても記載します。
182
199
183
200
### ` <*> ` を使って2つ目以降の引数を「命令」の結果から渡す
184
201
185
- ` + ` などの二項演算子といった、2つ以上の引数を受け取る関数の引数を 、` IO ` などの「命令」の結果から渡す場合 、` <$> ` だけではできません(理由は後述します)。
202
+ ` + ` などの二項演算子といった、2つ以上の引数を受け取る関数の引数として 、` IO ` などの「命令」の結果を渡す場合 、` <$> ` だけではできません(理由は後述します)。
186
203
187
- これまで下記のように ` do ` を使って書いていたものは、 ` <$> ` と ` <*> ` を使って書き換えることができます。
204
+ 例えばこれまで、下記のように ` do ` を使って書いていた場合を例としましょう:
188
205
189
- ```
206
+ ``` haskell
190
207
-- `getLine`を2回実行して、取得した2つの文字列を ++ で結合する
191
208
do
192
209
line1 <- getLine
193
210
line2 <- getLine
194
211
return $ line1 ++ line2
195
212
```
196
213
197
- ```
214
+ このように二つ(以上)の「命令」が返した結果を、命令でない、純粋な関数に渡すコードは、` <$> ` と` <*> ` を使って次のように書き換えることができます:
215
+
216
+ ``` haskell
198
217
(++) <$> getLine <*> getLine
199
218
```
200
219
201
- すっきり!やっぱりワンライナーで書けましたね!
202
- (課題18で学習した「演算子を前置関数に変換する」方法を思い出してください!)
220
+ すっきり!やっぱりワンライナーで書けましたね!(課題18で学習した「演算子を前置関数に変換する」方法を思い出してください!)
203
221
204
- 実行例
222
+ 試しに実行すると、確かに ` getLine ` を2回実行して、その結果を ` ++ ` で結合していることがわかります:
205
223
206
- ```
224
+ ``` haskell
207
225
ghci> (++) <$> getLine <*> getLine
208
226
first half -- ここはユーザーによる入力
209
227
second half -- ここはユーザーによる入力
210
228
" first halfsecond half"
211
229
```
212
230
213
- 例のごとく、新しい関数を見つけたら型を見る、の精神で、` <*> ` の型を覗いてみましょう。
231
+ 例のごとく、新しい関数を見つけたら型を見る、の精神で、` <*> ` の型を覗いてみましょう:
214
232
215
- ```
233
+ ``` haskell
216
234
ghci> : t (<*>)
217
235
(<*>) :: Applicative f => f (a -> b ) -> f a -> f b
218
236
```
219
237
220
- ` Monad ` , ` Functor ` と続いて、` Applicative ` というまた見知らぬ型クラスが登場しました。
221
- これらの詳細は次の課題で行いますので、今はまた` TypeApplications ` で` f ` を` IO ` に置き換えましょう。
238
+ `Monad `, `Functor `と続いて、 `Applicative `というまた見知らぬ型クラスが登場しました。 これらの詳細は次の課題で行いますので、 今はまた`TypeApplications `で`f` を`IO `に置き換えましょう。
222
239
223
- ```
240
+ ```haskell
224
241
ghci> : set - XTypeApplications
225
242
ghci> : t (<*>) @ IO
226
243
(<*>) @ IO :: IO (a -> b ) -> IO a -> IO b
227
244
```
228
245
246
+ ` TypeApplications ` で` f ` を` IO ` に絞っても、まだまだ複雑な型ですね。日本語で説明すると、` <*> ` は両辺に次のような値を受け取り、次のような値を返す演算子となっています:
247
+
229
248
- 第1引数(左辺)の` IO (a -> b) ` : 「普通の(入出力を行わない)関数 ` a -> b ` 」を返す命令。
230
249
- 第2引数(右辺)の` IO a ` : 「第1引数の命令が返す関数」の引数と同じ型` a ` を返す命令。
231
250
- 戻り値の` IO b ` : 「第1引数の命令が返す関数」の戻り値と同じ型` b ` を返す命令。
0 commit comments