Topic: Finish Databases Date: Oct. 28, 2009 Number: 17 Examples: Database.hs, NestedSquaresRead.hs Reading: -- Databases -- ** Last time demonstrated Database.hs. The notes are in the last lecture notes. -- I/O in Haskell -- We are going to take a break from looking at databases to to look at something that you need for PS 5: Haskell I/O. I/O in Haskell is something of a pain. The problem, as I pointed out before, is that I/O does not satisfy a basic premise of functional programming - a function gives the same result each time it is called. Certainly getLine fails this - every time you call it you get a different result. The solution is to use the IO monad. There is something that represents the "state" of the outside world. There is a consequence of this - once in the IO monad, there is no way to escape. I have heard it described as a black hole - stuff can be put in, but nothing comes out. So the natural way to get a number would be to prompt for it, read it, and convert it. This can be done, as seen in NestedSquaresRead.hs: putStr "Smallest radius = " small <- getLine let sm = read small Here "read" is the opposite of show - it takes the string and coverts it to a value. A natural thing to do is to make this a function: readInt :: String -> Int readInt prompt = do putStr prompt numStr <- getLine let num = read numStr num and then to call readInt from another function. THIS DOES NOT WORK! The problem is that the last statment in the do is not of type IO . (They don't want to let the value escape from the IO monad to pollute the pure functional world.) Instead, you have to call the functions to do what you want from within the IO do: main = runGraphics ( do w <- openWindow "Nested Squares" (300, 300) putStr "Smallest radius = " small <- getLine let sm = read small putStr "Largest radius = " large <- getLine let lg = read large putStr "Increment = " incr <- getLine let inc = read incr let nest1 = makeNestedSquares (150, 150) sm lg inc Red sequence_ (graphicsToIO w nest1) spaceClose w) Note - if not doing a <-, have to use a "let" on the assignment. Just like with the interpreter. There is a reason - the interpreter is basically one big "do" statement! Note - remember that <- takes an IO on the right and binds the value of to the name on the left. Note that I can call makeNestedSquares and pass sm, lg, and inc, but the results are returned to something within the do. Can also use a standard let, with assignments to names and an "in". It is possible to write a readInt, but you have to return an IO Int, not an Int: readInt :: String -> IO Int readInt prompt = do putStr prompt numStr <- getLine let num = read numStr return num Then you can write: main2 = runGraphics ( do w <- openWindow "Nested Squares" (300, 300) sm <- readInt "Smallest radius = " lg <- readInt "Largest radius = " inc <- readInt "Increment = " let nest1 = makeNestedSquares (150, 150) sm lg inc Red sequence_ (graphicsToIO w nest1) spaceClose w) You will have to do this with the Kevin Bacon game, also. All of the file reading, prompts to get values, etc. will have to be in a top-level do or a function that returns something of type IO something. So you read and convert the contents of the files, create the tables, and then call the graph and BFS functions from within a do or a function that returns something of type IO something (usually IO ()).