IO入門編

From HaskellWiki
Revision as of 14:15, 8 December 2009 by Ymotongpoo (talk | contribs)
Jump to navigation Jump to search

(このページは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!"

Now, so far, all this is great, but without a way to chain actions 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:

(>>) :: IO a -> IO b -> IO b

where if x and y are IO actions, then (x >> y) is the action that performs x, dropping the result, then performs y and returns its result. Great, we can now write programs which do multiple things:

main = putStrLn "Hello" >> putStrLn "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':

(>>=) :: IO a -> (a -> IO b) -> IO b

Now, x >>= f is the action that first performs the action x, and captures its result, passing it to f, 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:

main = putStrLn "Hello, what is your name?"
      >> getLine
      >>= \name -> putStrLn ("Hello, " ++ name ++ "!")

This is most of what we need. In fact, this bind function is really successful, and we can define (>>) in terms of it:

x >> y = x >>= const y

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,

return :: a -> IO a

which does just that.

You might see do-notation all over the place in real Haskell programs. In do-notation, our example program might look like:

main = do putStrLn "Hello, what is your name?"
          name <- getLine
          putStrLn ("Hello, " ++ name ++ "!")

This is in fact entirely equivalent to the above form, and is translated into it by the Haskell compiler. So whenever you see a do block, you can just imagine a chain of applications of (>>) and (>>=), 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 v <- x will cause the action x to be run, and the result bound to the variable v.

A common mistake is to put something other than an action in the place of x, 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 let a = b, which, like an ordinary let-expression will define a to be the same as b, but the definition scopes over the remainder of the do-block.

Note that there is no function:

unsafe :: IO a -> a

as this would defeat the referential transparency of Haskell -- applying unsafe to the same IO action might return different things every time, and Haskell functions aren't allowed to behave that way.

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.

- CaleGibbard

さらに読みたい方に

Languages: en