200 likes | 380 Views
Haskell. Chapter 8. Input and Output. What are I/O actions? How do I/O actions enable us to do I/O? When are I/O actions actually performed?. Review of Functional Language characteristics. Haskell is purely functional
E N D
Haskell Chapter 8
Input and Output • What are I/O actions? • How do I/O actions enable us to do I/O? • When are I/O actions actually performed?
Review of Functional Language characteristics • Haskell is purely functional • Provide definitions of what things are, Haskell figures out the steps to execute • Functions are not allowed to have side effects • If a function is called two times with the same parameters, it must return the same result (referential transparency) • Helps us reason about our programs (e.g., don’t have to trace entire program to see if some other function is messing with your variables!)
I/O • But what about showing the result? That changes the state of the screen… a side effect! • What to do??? • Separate the “impure” parts of the program from the “pure” parts.
Our first program main = putStrLn "Hello World!" At the command prompt • ghc –make helloworld • helloworld (windows) or ./helloworld (nix)
What’s putStrLn? • Prelude> :t putStrLn • putStrLn :: String -> IO () • Prelude> :t putStrLn "hello, world" • putStrLn "hello, world" :: IO () • putStr take a String, returns an IO action • An IO action does some action with a side effect (e.g., read input or write output) and also yields a result • The value of putStrLn is just ()
When are I/O actions performed? • Must give it a name of main and then run the program • Can glue several I/O actions into one. • Don’t usually specify a type declaration for main – it has signature of main :: IO something where something is a concrete type, for example: • *Main> :t main • main :: IO () • getLine is an IO action that yields a String • getLine is impure • *Main> :t getLine • getLine :: IO String
So what exactly is an IO action?? • You can think of it as a box with feet. • Goes out into the world and does something • Maybe brings something back • Use <- to “open” the box and get the value • We can only take it out when we’re inside another IO action • This won’t work!! • nameTag = "Hello, my name is " ++ getLine
How do the pure and impure parts interact? main2 = do putStrLn "Hello, what's your name?" name <- getLine putStrLn $ "Zis is your future: " ++ tellFortune name tellFortune name | name == "Lucky" = name ++ ",you will win a million dollars" | otherwise = name ++ ", you will have no money but be happy" • *Main> main2 • Hello, what's your name? • Lucky • Zis is your future: Lucky,you will win a million dollars • tellFortune is just a regular function! • *Main> tellFortune "Cyndi" • "Cyndi, you will have no money but be happy"
How does this work in GHCi? • *Main> putStrLn "howdy pardner" • howdy pardner • In GHCi, it performs the IO action and then prints it to the terminal using putStrLn
Bindings import Data.Char main3 = do putStrLn "What's your first name? " firstName <- getLine putStrLn "What's your last name? " lastName <- getLine let bigFirstName = map toUpperfirstName let bigLastName = map toUpperlastName putStrLn $ "hey " ++ bigFirstName ++ " " ++ bigLastName ++ ", how are you?“ • <- binds values from IO actions to names • let binds pure expressions to names
More details main4 = do line <- getLine if null line then return () else do putStrLn $ reverseWords line main4 reverseWords :: String -> String reverseWords = unwords . map reverse . words • remember: if must always have else • return() is not like other languages. return makes an I/O action out of a pure value (i.e., takes a value and “wraps” it in an IO action box). Return does not end execution.
Return example main5 = do return () return "HAHAHA" line <- getLine return "BLAH BLAH" return 4 putStrLn line • Creates lots of I/O actions that are not used
More on return main6 = do a <- return "Hello" b <- return "World" putStrLn $ a ++ " " ++ b • More likely to use let in this scenario… but it illustrates how return works • Common to use return when last statement of do doesn’t have desired result. So create a return that yields the desired result, put it at the end • Also used when we need an IO action that doesn’t do anything (like the return () in main4)
Other useful functions • putStr – like putStrLn, but no newline • putChar – takes a character and returns an IO action that will print it to the terminal - putChar 'a' • print – takes a value that’s an instance of Show (i.e., we know how to represent as string), applies show to stringify it, outputs string to terminal - print "haha"
when • when – syntactic sugar. Takes Bool & I/O, returns I/O • when :: Monad m => Bool -> m () -> m () import Control.Monad main7 = do input <- getLine if (input == "SWORDFISH") then putStrLn input else return () main8 = do input <- getLine when (input == "SWORDFISH") $ do putStrLn input
sequence • sequence takes a list of I/O actions, returns an I/O action that performs those actions one after the other main9 = do a <- getLine b <- getLine c <- getLine print [a,b,c] main10 = do rs <- sequence [getLine, getLine, getLine] print rs
mapM • Common scenario: map a function that returns an I/O action (e.g., print) over a list and then sequence it. mapM does this for us, mapM_ does it but throws away the result. • *Main> mapM print [1,2,3] • 1 • 2 • 3 • [(),(),()] • *Main> mapM_ print [1,2,3] • 1 • 2 • 3
Other useful functions in book • forever • forM
Play and Share? • Let’s start on the homework