Concepts of Higher Programming Languages Chapter 14: Monads In Use IO Jonathan Thaler Department of Computer Science 1 / 35
Monads in Use 2 / 35
Monads in Use: IO Haskell Executable Entry Point Just as other programming languages, Haskell programs also have an entry point , which is invoked when the program is started : main :: IO () IO is a monadic type defined in the IO Monad . Therefore, main is a monadic action , which when executed will produce an empty tuple as output and side effects within the IO Monad . The IO Monad is the window to the real world : it allows all kind of interactions with the real world using impure side effects such as console access, file IO, references, concurrency, networking,... IO is an opaque type but we can understand it as a computation , which acts on the state of the world : type IO a = World -> (a, World) 3 / 35
Monads in Use: IO IO Monad Monadic actions of the IO Monad can only be executed from within IO actions originating from the top-level main :: IO () The actual execution of the resulting main :: IO () action, happens through the Haskell Runtime System which is linked against each executable. GHCi implicitly runs within the IO Monad. This is why we could print results of our exercises. 4 / 35
Monads in Use IO: Console 5 / 35
Monads in Use: Console The standard library provides a number of actions for the Console: The action getChar reads a character from the keyboard, echoes it to the screen, and returns the character as its result value: getChar :: IO Char The action putChar writes a character c to the screen, and returns no result value: putChar :: Char -> IO () 6 / 35
Monads in Use: Console An action reading a line from the console : getLine :: IO String getLine = do x <- getChar if x == '\n' then return [] else do xs <- getLine return (x:xs) 7 / 35
Monads in Use: Console Writing a string to the screen : putStr :: String -> IO () putStr [] = return () putStr (x:xs) = do putChar x putStr xs Writing a string and moving to a new line : putStrLn :: String -> IO () putStrLn xs = do putStr xs putChar '\n' 8 / 35
Monads in Use: Console An action that prompts for a string to be entered and displays its length : strlen :: IO () strlen = do putStr "Enter a string: " xs <- getLine putStr "The string has " putStr (show (length xs)) putStrLn " characters" > strlen Enter a string: Haskell The string has 7 characters 9 / 35
Monads in Use: Console Consider the following version of Hangman: 1. One player secretly types in a word. 2. The other player tries to deduce the word, by entering a sequence of guesses. 3. For each guess, the computer indicates which letters in the secret word occur in the guess. 4. The game ends when the guess is correct. 10 / 35
Monads in Use: Console Start with the top level IO action in main :: IO () : main :: IO () main = do putStrLn "Think of a word: " word <- sgetLine -- see next slide... putStrLn "Try to guess it:" play word 11 / 35
Monads in Use: Console The action sgetLine reads a line of text from the keyboard echoing each char- acter as a dash sgetLine :: IO String sgetLine = do x <- getCh -- see next slide... if x == '\n' then do putChar x return [] else do putChar '-' xs <- sgetLine return (x:xs) 12 / 35
Monads in Use: Console The action getCh reads a single character from the keyboard without echoing it to the screen import System.IO getCh :: IO Char getCh = do hSetEcho stdin False x <- getChar hSetEcho stdin True return x 13 / 35
Monads in Use: Console The function play is the main loop which requests and processes guesses until the game ends play :: String -> IO () play word = do putStr "? " guess <- getLine if guess == word then putStrLn "You got it!" else do putStrLn (match word guess) -- see next slide... play word 14 / 35
Monads in Use: Console The function match indicates which characters in one string occur in a second string match :: String -> String -> String match xs ys = map (\x -> if elem x ys then x else '-') xs > match "haskell" "pascal" "-as--ll" 15 / 35
Monads in Use: Console Running Hangman from GHCi $ ghci Hangman.hs ... > main Think of a word: ------- Try to guess it: ? H H ------ ? l -----ll ? Haskell You got it! > 16 / 35
Monads in Use: Console Running Hangman as binary executable $ ghc Hangman.hs [1 of 1] Compiling Main ( Hangman.hs, Hangman.o ) Linking Hangman ... $ ./Hangman or Hangman.exe Think of a word: ------- Try to guess it: ? H H ------ ? l -----ll ? Haskell You got it! $ 17 / 35
Monads in Use IO: File Access 18 / 35
Monads in Use: File Access The standard library provides a number of actions for file access: The readFile function reads a file and returns the contents of the file as a string. The file is read lazily, on demand: readFile :: FilePath -> IO String The action writeFile writes a string str , to a file: writeFile :: FilePath -> String -> IO () The action appendFile appends a string str , to a file: appendFile :: FilePath -> String -> IO () type FilePath = String 19 / 35
Monads in Use: File Access A program which copies files import System.Environment main :: IO () main = do [from, to] <- getArgs -- reads arguments from the command line inFile <- readFile from writeFile to inFile 20 / 35
Monads in Use: File Access The standard library provides a number of additional actions for file access: The action openFile allocates and returns a new, open handle to manage the file file: openFile :: FilePath -> IOMode -> IO Handle The action hPutStrLn writes a string to the file or channel managed by hdl, followed by a newline character: hPutStrLn :: Handle -> String -> IO () The action hFlush causes any items buffered for output in the handle to be sent immediately to the operating system: hFlush :: Handle -> IO () 21 / 35
Monads in Use: File Access A program which writes input to a file forever import System.IO import System.Environment import Control.Monad main :: IO () main = do [to] <- getArgs hdl <- openFile to WriteMode forever (do str <- getLine hPutStrLn hdl str hFlush hdl) 22 / 35
Monads in Use IO: Random 23 / 35
Monads in Use: Random IO Haskell offers random numbers also in the IO Monad through a global random-number generator : randomIO :: (Random a, MonadIO m) => m a is the monadic variant of random using the global random-number generator. randomRIO :: (Random a, MonadIO m) => (a, a) -> m a is the monadic variant of randomR using the global random-number generator. setStdGen :: MonadIO m => StdGen -> m () allows to set the global random-number generator. 24 / 35
Monads in Use: Random IO A simple random IO example import System.Random main :: IO () main = do x <- randomRIO (1,6) :: IO Int y <- randomIO :: IO Int print x print y fixRNGAndDraw (1,6) fixRNGAndDraw (1,6) fixRNGAndDraw :: (Int, Int) -> IO () fixRNGAndDraw r = do setStdGen (mkStdGen 42) x1 <- randomRIO r x2 <- randomRIO r print x1 print x2 25 / 35
Monads in Use IO: References 26 / 35
Monads in Use: References Haskell also offers mutable references : The IORef type resides in the Data.IORef module, which needs to be imported. The action newIORef creates a new mutable reference and initialises it with an initial value: newIORef :: a -> IO (IORef a) The action readIORef reads from a mutable reference: readIORef :: IORef a -> IO a The action writeIORef writes to a mutable reference: writeIORef :: IORef a -> a -> IO () The action modifyIORef modifies a mutable reference: modifyIORef :: IORef a -> (a -> a) -> IO () 27 / 35
Monads in Use: References An imperative factorial import Data.IORef main :: IO () main = do ref <- newIORef 1 mapM_ (factorialImperative ref) [1..10] s <- readIORef ref print s factorialImperative :: IORef Integer -> Integer -> IO () factorialImperative ref i = do x <- readIORef ref let x' = x * i writeIORef ref x' factorialImperative' :: IORef Integer -> Integer -> IO () factorialImperative' ref i = modifyIORef ref (*i) 28 / 35
Monads in Use The IO Monad offers a lot more : Various mutable arrays 1 System time, CPU time, date, number of cores, interpreter,... Foreign Function Call (FFC) interface to call into C code. All real-world code runs from within the IO Monad: DB, Web,... 1 http://hackage.haskell.org/package/array-0.5.4.0 29 / 35
Monads in Use IO Conclusion 30 / 35
Monads in Use Pure vs. Impure Side Effects We have to distinguish between pure and impure side effects: Pure : are all side effects in monads which are implemented as pure computations: State, Reader, Writer, Random, Maybe, List, Either ,... Their side effects are completely controlled and defined by pure computations, do not leak out of the pure computations. The results of computations with pure side effects are always deterministic . Impure : are all side effects happening in monads which interact with the real world: IO, STM (see next chapter). Their side effects are not controllable because they interact with the real world. The results of computations with impure side effects are in general not deterministic . 31 / 35
Recommend
More recommend