Difference between revisions of "IO入門編"
Ymotongpoo (talk | contribs) |
m (→さらに読みたい方に: Redirected link to the Web Archive) |
||
(4 intermediate revisions by 4 users not shown) | |||
Line 18: | Line 18: | ||
</haskell> |
</haskell> |
||
+ | ここまでいい感じにきていますが、エンド・トゥ・エンドのチェイン・アクションなしでは多くのことはできません。なので、HaskellではIOアクションを繋げる2、3の原則を提示しています。 |
||
− | Now, so far, all this is great, but without a way to chain actions |
||
+ | そのうちの1つは以下の例で表されます。 |
||
− | together end-to-end, we can't do a whole lot. So Haskell provides us |
||
− | with a few primitives for composing and chaining together IO actions. |
||
− | A simple one of these is: |
||
<haskell> |
<haskell> |
||
(>>) :: IO a -> IO b -> IO b |
(>>) :: IO a -> IO b -> IO b |
||
</haskell> |
</haskell> |
||
− | + | もし<code>x</code> と <code>y</code>がIOアクションなら、<code>(x >> y)</code>は<code>x</code>を実行し、結果を破棄し、そのあと<code>y</code>を実行してその結果を返します。 |
|
+ | |||
− | performs <code>x</code>, dropping the result, then performs <code>y</code> and returns its |
||
+ | すごい、これでもう複数の手続きがあるプログラムが書けてしまいます。 |
||
− | result. |
||
− | Great, we can now write programs which do multiple things: |
||
<haskell> |
<haskell> |
||
main = putStrLn "Hello" >> putStrLn "World" |
main = putStrLn "Hello" >> putStrLn "World" |
||
</haskell> |
</haskell> |
||
+ | このコードは"Hello"と"World"を別々の行に表示します。 |
||
− | will print "Hello" and "World" on separate lines. |
||
+ | しかしながら、まだ最初の結果を用いて次の結果が何をするか決めるようなチェイン・アクションの書き方が分かっていません。これは次に示すような 'バインド(束縛)' と呼ばれる操作で実現できます。 |
||
− | However, we don't yet have a way to chain actions in which we are |
||
− | allowed to use the result of the first in order to affect what the |
||
− | second action will do. This is accomplished by the following |
||
− | operation, called 'bind': |
||
<haskell> |
<haskell> |
||
(>>=) :: IO a -> (a -> IO b) -> IO b |
(>>=) :: IO a -> (a -> IO b) -> IO b |
||
</haskell> |
</haskell> |
||
+ | ここで<code>x >>= f</code>は最初にアクション<code>x</code>を実行して、その結果をつかんで、<code>f</code>に渡してそれが次のアクションが実行される、という一連のアクションを表しています。一連のアクションが実行されると、その結果は全体の計算結果となります。 |
||
− | Now, <code>x >>= f</code> is the action that first performs the action <code>x</code>, and captures its result, passing it to <code>f</code>, which then computes a second action to be performed. That action is then carried out, and its result is the result of the overall computation. |
||
+ | 説明が長くなってしまいましたが、百聞は一見にしかずで、使用例を見ればすぐにわかると思います。 |
||
− | That's a mouthful, but once you see it in use, perhaps the idea will |
||
− | become clearer: |
||
<haskell> |
<haskell> |
||
main = putStrLn "Hello, what is your name?" |
main = putStrLn "Hello, what is your name?" |
||
Line 51: | Line 44: | ||
</haskell> |
</haskell> |
||
+ | これこそ必要としていたものでした。実際この束縛関数は非常によくできていて、先ほどの<code>(>>)</code>も次のように定義できます。 |
||
− | This is most of what we need. In fact, this bind function is really |
||
− | successful, and we can define <code>(>>)</code> in terms of it: |
||
<haskell> |
<haskell> |
||
x >> y = x >>= const y |
x >> y = x >>= const y |
||
</haskell> |
</haskell> |
||
+ | 実際に、ある値をなにもしないでそのまま値を返すようなIOアクションに変換することも非常に大事だとわかりました。これはチェイン・アクションの最後のほうで使うと非常に便利です。これによって、チェインの最後のアクションに処理をゆだねるのではなく、自分たちでどう処理して何を返すか決定することができます。より原始的には |
||
− | In practice, it turns out to also be quite important to turn a value |
||
− | into an IO action which does nothing, and simply returns that value. |
||
− | This is quite handy near the end of a chain of actions, where we want |
||
− | to decide what to return ourselves, rather than leaving it up to the |
||
− | last action in the chain. So there's one more primitive, |
||
<haskell> |
<haskell> |
||
return :: a -> IO a |
return :: a -> IO a |
||
</haskell> |
</haskell> |
||
+ | と書け、いま言ったようなことができます。 |
||
− | which does just that. |
||
+ | do記法はあらゆるHaskellプログラムで見ることができます。do記法を用いた場合、先ほどの例は次のようになります: |
||
− | You might see do-notation all over the place in real Haskell programs. In |
||
− | do-notation, our example program might look like: |
||
<haskell> |
<haskell> |
||
Line 76: | Line 63: | ||
</haskell> |
</haskell> |
||
+ | do記法を使った例は実際にdo記法を使わない例と全く同じことであり、Haskellコンパイラによってdo記法を同じものにコンパイルされる。なので、doブロックを見たら、<code>(>>)</code> と <code>(>>=)</code>の連鎖とアクションの結果を取得するのに適切なラムダ式を想像してみてください。 |
||
− | This is in fact entirely equivalent to the above form, and is |
||
+ | doブロック内の各行にある各アクションは普通に実行され、<code>v <- x</code>となっている行ではアクション<code>x</code>を実行し、結果を変数<code>v</code>に割り当てます。 |
||
− | translated into it by the Haskell compiler. So whenever you see a do |
||
− | block, you can just imagine a chain of applications of <code>(>>)</code> and <code>(>>=)</code>, |
||
− | and some lambdas when appropriate to capture the results of actions. An action on its own on a line in a do-block will be executed, and a line of the form <code>v <- x</code> will cause the action <code>x</code> to be run, and the result bound to the variable <code>v</code>. |
||
+ | よく<code>x</code>の位置にアクションでないものを書いてしまうミスをすることがあります。ありがちなのは値を書くことです。もしdoブロック内で変数への束縛をアクションなしに行いたい場合は、<code>let a = b</code>という書き方をすればできます。これは一般的なlet式のように<code>a</code>を<code>b</code>を同じものと定義しますが、doブロック外のスコープには適用されません。 |
||
− | A common mistake is to put something other than an action in the place of <code>x</code>, usually some other value. If you want to make a variable binding inside a do-block which doesn't involve running an action, then you can use a line of the form <code>let a = b</code>, which, like an ordinary let-expression will define <code>a</code> to be the same as <code>b</code>, but the definition scopes over the remainder of the do-block. |
||
+ | 次のような関数はないことに気を付けてください: |
||
− | Note that there is no function: |
||
<haskell> |
<haskell> |
||
unsafe :: IO a -> a |
unsafe :: IO a -> a |
||
</haskell> |
</haskell> |
||
+ | 理由は簡単で、これを許してしまうとHaskellの参照透過性が侵されてしまうからです。―たとえば、<code>unsafe</code>を同じIOアクションに適用しても毎回異なる結果を返します。そしてこれはHaskellの関数にはあってはならない振る舞いです。 |
||
− | as this would defeat the referential transparency of Haskell -- |
||
− | applying <code>unsafe</code> to the same IO action might return different things |
||
− | every time, and Haskell functions aren't allowed to behave that way. |
||
+ | まだモナドについては一般的なことはほとんどお伝えできていません。ほとんどのモナドは実際はIOとは似ていませんが、バインドとreturnに関しては同じようなコンセプトを持っています。より一般的にモナドを学びたい場合は [[Monads as containers]] の記事か [[Monads as computation]]の記事を読んでみてください。2つの記事はそれぞれモナドを異なる側面から大まかに説明しています。 |
||
− | Now, I haven't really told you anything about monads in general yet. |
||
− | Most monads are actually rather unlike IO, but they do share the |
||
− | similar concepts of bind and return. For more on monads in general, |
||
− | see my [[Monads as containers]] article, or my [[Monads as computation]] article which give two different ways to look at what monads abstract. |
||
- [[User:CaleGibbard|CaleGibbard]] |
- [[User:CaleGibbard|CaleGibbard]] |
||
Line 100: | Line 80: | ||
== さらに読みたい方に == |
== さらに読みたい方に == |
||
− | * |
+ | * IOモナドを用いたより理解しやすいチュートリアルを見たい方は [http://haskell.org/haskellwiki/IO_inside Haskell I/O inside: Down the Rabbit's Hole] を読んでみてください |
− | * |
+ | * 基本的なモナド関数の説明と例はリファレンスガイド [https://web.archive.org/web/20201109033750/members.chello.nl/hjgtuyl/tourdemonad.html A tour of the Haskell Monad functions]にあります。(Henk-Jan van Tuyl著) |
− | * |
+ | * さらにこちらも参照してみてください。 [[Avoiding IO|how to avoid IO]] |
[[Category:Idioms]] |
[[Category:Idioms]] |
||
[[Category:Monad]] |
[[Category:Monad]] |
||
+ | [[Category:Jp]] |
||
Languages: [[Introduction to IO|en]] |
Languages: [[Introduction to IO|en]] |
Latest revision as of 11:39, 22 October 2022
(このページはHaskellではIOがどのように扱われているかを手っ取り早く紹介することを目的としています。学ぶべきことすべてはお伝えできませんが、どのように動作しているかを感覚はつかめると思います。)
Haskellでは、副作用は特定の型の値としてエンコードすることで考慮されなければならない、とすることで副作用がある処理と純粋な関数を切り離してきました。(IO a)
型の値はアクションです。これは実行されたらa
という型の値を生成しますよ、ということを表しています。
いくつか例を見てみましょう:
getLine :: IO String
putStrLn :: String -> IO () -- note that the result value is an empty tuple.
randomRIO :: (Random a) => (a,a) -> IO a -- in practice you will often avoid IO and prefer randomR
ふつうHaskellの評価ではこのような処理は起き得ません。(IO a)
型の値はほぼ実行されないまま残ります。実際、コンパイルされたHaskellプログラム中で本当に実行されるように命令されるIOアクションはmain
だけです。
このような前提を踏まえて、"hello, world"プログラムは次のように書けます。
main :: IO ()
main = putStrLn "Hello, World!"
ここまでいい感じにきていますが、エンド・トゥ・エンドのチェイン・アクションなしでは多くのことはできません。なので、HaskellではIOアクションを繋げる2、3の原則を提示しています。 そのうちの1つは以下の例で表されます。
(>>) :: IO a -> IO b -> IO b
もしx
と y
がIOアクションなら、(x >> y)
はx
を実行し、結果を破棄し、そのあとy
を実行してその結果を返します。
すごい、これでもう複数の手続きがあるプログラムが書けてしまいます。
main = putStrLn "Hello" >> putStrLn "World"
このコードは"Hello"と"World"を別々の行に表示します。
しかしながら、まだ最初の結果を用いて次の結果が何をするか決めるようなチェイン・アクションの書き方が分かっていません。これは次に示すような 'バインド(束縛)' と呼ばれる操作で実現できます。
(>>=) :: IO a -> (a -> IO b) -> IO b
ここでx >>= f
は最初にアクションx
を実行して、その結果をつかんで、f
に渡してそれが次のアクションが実行される、という一連のアクションを表しています。一連のアクションが実行されると、その結果は全体の計算結果となります。
説明が長くなってしまいましたが、百聞は一見にしかずで、使用例を見ればすぐにわかると思います。
main = putStrLn "Hello, what is your name?"
>> getLine
>>= \name -> putStrLn ("Hello, " ++ name ++ "!")
これこそ必要としていたものでした。実際この束縛関数は非常によくできていて、先ほどの(>>)
も次のように定義できます。
x >> y = x >>= const y
実際に、ある値をなにもしないでそのまま値を返すようなIOアクションに変換することも非常に大事だとわかりました。これはチェイン・アクションの最後のほうで使うと非常に便利です。これによって、チェインの最後のアクションに処理をゆだねるのではなく、自分たちでどう処理して何を返すか決定することができます。より原始的には
return :: a -> IO a
と書け、いま言ったようなことができます。
do記法はあらゆるHaskellプログラムで見ることができます。do記法を用いた場合、先ほどの例は次のようになります:
main = do putStrLn "Hello, what is your name?"
name <- getLine
putStrLn ("Hello, " ++ name ++ "!")
do記法を使った例は実際にdo記法を使わない例と全く同じことであり、Haskellコンパイラによってdo記法を同じものにコンパイルされる。なので、doブロックを見たら、(>>)
と (>>=)
の連鎖とアクションの結果を取得するのに適切なラムダ式を想像してみてください。
doブロック内の各行にある各アクションは普通に実行され、v <- x
となっている行ではアクションx
を実行し、結果を変数v
に割り当てます。
よくx
の位置にアクションでないものを書いてしまうミスをすることがあります。ありがちなのは値を書くことです。もしdoブロック内で変数への束縛をアクションなしに行いたい場合は、let a = b
という書き方をすればできます。これは一般的なlet式のようにa
をb
を同じものと定義しますが、doブロック外のスコープには適用されません。
次のような関数はないことに気を付けてください:
unsafe :: IO a -> a
理由は簡単で、これを許してしまうとHaskellの参照透過性が侵されてしまうからです。―たとえば、unsafe
を同じIOアクションに適用しても毎回異なる結果を返します。そしてこれはHaskellの関数にはあってはならない振る舞いです。
まだモナドについては一般的なことはほとんどお伝えできていません。ほとんどのモナドは実際はIOとは似ていませんが、バインドとreturnに関しては同じようなコンセプトを持っています。より一般的にモナドを学びたい場合は Monads as containers の記事か Monads as computationの記事を読んでみてください。2つの記事はそれぞれモナドを異なる側面から大まかに説明しています。
さらに読みたい方に
- IOモナドを用いたより理解しやすいチュートリアルを見たい方は Haskell I/O inside: Down the Rabbit's Hole を読んでみてください
- 基本的なモナド関数の説明と例はリファレンスガイド A tour of the Haskell Monad functionsにあります。(Henk-Jan van Tuyl著)
- さらにこちらも参照してみてください。 how to avoid IO
Languages: en