[Work on IO Monad John Goerzen **20070710054304] { hunk ./en/ch06-io.xml 890 - - Most languages do not make a distinction between a pure function and an - impure one. Haskell has functions in the mathematical sense: they are - purely computations which cannot be impacted by anything external. - Moreover, the computation can be performed at any time -- or even - never, if its result is never needed. - - - Clearly, then, we need some other tool to work with I/O. That tool in - Haskell is called actions. Actions resemble - functions. They do nothing when they are defined, but perform some - task when they are invoked. I/O actions are defined within the &IO; - monad. Monads are actually a powerful way of chaining data together - purely and are covered in FIXME: insert ref. It's not - necessary to understand monads in order to understand I/O. Just - understand that the return value of actions is "tagged" with - &IO;. Let's take a look at some types: - - &basicio.ghci:all; - - The type of &putStrLn; is just another function. The function takes - one parameter and returns an IO (). This - IO () is the action. You can store and pass actions - in pure code, though this is rarely done. An action doesn't do - anything until it is invoked. Let's look at an example of this: - - &actions.hs:all; - - str2action is a function that takes one parameter - and returns an IO (). As you can see at the end of - &main;, you could use this directly in another action and it will print - out a line right away. But you can store -- but not execute -- the - action from pure code. You can see an example of that in - list2actions -- we use ↦ over - str2action and return a list of actions, just like - we would with other pure data. You can see that everything up through - printitall is built up with pure tools. - - - Although we define printitall, it doesn't get - executed until its action is evaluated somewhere else. Notice in - main how we use str2action as - an I/O action to be executed, but earlier we used it outside of the I/O - monad and assembled results into a list. - - - You could think of it this way: every statement, except &let;, in a &do; block must - yeild an I/O action which will be executed. - - - The call to &printall; finally executes all those actions. Actually, - since Haskell is lazy, the actions aren't generated until here either. - - - When you run the program, your output will look like this: - - + + Actions + + Most languages do not make a distinction between a pure function and an + impure one. Haskell has functions in the mathematical sense: they are + purely computations which cannot be impacted by anything external. + Moreover, the computation can be performed at any time -- or even + never, if its result is never needed. + + + Clearly, then, we need some other tool to work with I/O. That tool in + Haskell is called actions. Actions resemble + functions. They do nothing when they are defined, but perform some + task when they are invoked. I/O actions are defined within the &IO; + monad. Monads are actually a powerful way of chaining data together + purely and are covered in FIXME: insert ref. It's not + necessary to understand monads in order to understand I/O. Just + understand that the return value of actions is "tagged" with + &IO;. Let's take a look at some types: + + &basicio.ghci:all; + + The type of &putStrLn; is just another function. The function takes + one parameter and returns an IO (). This + IO () is the action. You can store and pass actions + in pure code, though this is rarely done. An action doesn't do + anything until it is invoked. Let's look at an example of this: + + &actions.hs:all; + + str2action is a function that takes one parameter + and returns an IO (). As you can see at the end of + &main;, you could use this directly in another action and it will print + out a line right away. But you can store -- but not execute -- the + action from pure code. You can see an example of that in + list2actions -- we use ↦ over + str2action and return a list of actions, just like + we would with other pure data. You can see that everything up through + printitall is built up with pure tools. + + + Although we define printitall, it doesn't get + executed until its action is evaluated somewhere else. Notice in + main how we use str2action as + an I/O action to be executed, but earlier we used it outside of the I/O + monad and assembled results into a list. + + + You could think of it this way: every statement, except &let;, in a &do; block must + yeild an I/O action which will be executed. + + + The call to &printall; finally executes all those actions. Actually, + since Haskell is lazy, the actions aren't generated until here either. + + + When you run the program, your output will look like this: + + hunk ./en/ch06-io.xml 961 - - - We can actually write this in a much more compact way. Consider this - revision of the example: - - &actions2.hs:all; - - Notice in str2action the use of the standard - function composition operator. In &main;, there's a call to &mapM_;. - This function is similar to ↦. It takes a function and a list. - The function supplied to &mapM_; is an I/O action that is executed for - every item in the list. &mapM_; throws out the result of the function, - though you can use &mapM; to return a list of I/O results if you want - them. Take a look at their types: - - &map.ghci:all; - - These functions actually work for more than just I/O; they work for any - &Monad;. For now, wherever you see "m", just think "IO". - - + + + We can actually write this in a much more compact way. Consider this + revision of the example: + + &actions2.hs:all; + + Notice in str2action the use of the standard + function composition operator. In &main;, there's a call to &mapM_;. + This function is similar to ↦. It takes a function and a list. + The function supplied to &mapM_; is an I/O action that is executed for + every item in the list. &mapM_; throws out the result of the function, + though you can use &mapM; to return a list of I/O results if you want + them. Take a look at their types: + + &map.ghci:all; + + These functions actually work for more than just I/O; they work for any + &Monad;. For now, wherever you see "m", just think "IO". + + + + Sequencing + + We mentioned earlier that &do; blocks are a shortcut notation. This + is true. There are two operators that you can use instead of &do; + blocks: &>>; and &>>=;. Let's look at their types in &ghci;: + + &sequence.ghci:all; + + &>>; sequences two actions together: the first action is performed, + then the second, and the result of the two will be the result of the + second. The result of the first is thrown away. This is similar to + simply having a new line in a &do; block. + + + &>>=; runs an action, passes its result to a function that returns an + action. That action is run as well, and the result of the entire + expression is the result of that second action. + + + Let's re-write one of our examples to avoid &do; blocks. Remember + this example from the start of the chapter? + + &basicio.hs:all.noid; + + Let's write that without a &do; block: + + &basicio-nodo.hs:all; + + The Haskell compiler is internally performing a translation just like + this when you define a &do; block. + + addfile ./examples/ch06/basicio-nodo.hs hunk ./examples/ch06/basicio-nodo.hs 1 +{-- snippet all --} +-- ch06/basicio-nodo.hs + +main = + putStrLn "Greetings! What is your name?" >> + getLine >>= + (\inpStr -> putStrLn $ "Welcome to Haskell, " ++ inpStr ++ "!") +{-- /snippet all --} addfile ./examples/ch06/map.ghci hunk ./examples/ch06/map.ghci 1 +--# all +:t mapM +:t mapM_ addfile ./examples/ch06/sequence.ghci hunk ./examples/ch06/sequence.ghci 1 +--# all +:t (>>) +:t (>>=) }