Concepts of Higher Programming Languages Chapter 14: Monads In Use - - PowerPoint PPT Presentation

concepts of higher programming languages chapter 14
SMART_READER_LITE
LIVE PREVIEW

Concepts of Higher Programming Languages Chapter 14: Monads In Use - - PowerPoint PPT Presentation

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


slide-1
SLIDE 1

Concepts of Higher Programming Languages Chapter 14: Monads In Use IO

Jonathan Thaler

Department of Computer Science

1 / 35

slide-2
SLIDE 2

Monads in Use

2 / 35

slide-3
SLIDE 3

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

slide-4
SLIDE 4

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

slide-5
SLIDE 5

Monads in Use IO: Console

5 / 35

slide-6
SLIDE 6

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

slide-7
SLIDE 7

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

slide-8
SLIDE 8

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

slide-9
SLIDE 9

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

slide-10
SLIDE 10

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

slide-11
SLIDE 11

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

slide-12
SLIDE 12

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

slide-13
SLIDE 13

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

slide-14
SLIDE 14

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

slide-15
SLIDE 15

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

slide-16
SLIDE 16

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

slide-17
SLIDE 17

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

slide-18
SLIDE 18

Monads in Use IO: File Access

18 / 35

slide-19
SLIDE 19

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

slide-20
SLIDE 20

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

slide-21
SLIDE 21

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

slide-22
SLIDE 22

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

slide-23
SLIDE 23

Monads in Use IO: Random

23 / 35

slide-24
SLIDE 24

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

slide-25
SLIDE 25

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

slide-26
SLIDE 26

Monads in Use IO: References

26 / 35

slide-27
SLIDE 27

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

slide-28
SLIDE 28

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

slide-29
SLIDE 29

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,...

1http://hackage.haskell.org/package/array-0.5.4.0 29 / 35

slide-30
SLIDE 30

Monads in Use IO Conclusion

30 / 35

slide-31
SLIDE 31

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

  • f 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

slide-32
SLIDE 32

Monads in Use

After the IO Monad you might think that: Monads are impure - not true. A monadic function is a pure function which returns an action when executed results in a side effect along side a result. The side effects of the IO Monad are not under the control of the program whereas the side effects of most other Monads are. Therefore there is nothing impure about monads in general. Monads are all about sequencing - not true. While monads are often used for sequencing actions in a way that looks like imperative programming, there are commutative monads that do not order actions.

32 / 35

slide-33
SLIDE 33

Monads in Use

The issue with the IO Monad: It covers too much. As soon as we are in the IO Monad, everything goes. With Type Classes it is possible to work around this issue but this is beyond the scope of this lecture. Therefore, as soon as we are in the IO Monad, we lose all guarantees of which side effects are / not happening. As a consequence structure Haskell code around pure and impure parts, which makes reasoning and testing much more easier. This issue is the same in imperative, OO languages, but Haskell makes this fundamental issue of programming explicit. Explicit Software Engineering principles with Haskell have only started to develop and exist mostly as implicit knowledge within the industry.

33 / 35

slide-34
SLIDE 34

Exercises

(1) Reproduce all the IO examples both with GHCi and GHC as executables. Play a round of Hangman with your friends! (2) Take the key-value store from Exercise 2 and write a polymorphic IO action which prints any key-value store to the console. The format is ”KEY:VALUE” for each key-value pair in a new line. Make sure it works for all key-value stores which keys and values are printable. Hint: a type which is printable implements the Show type class, which provides the method show :: a -> String to transform a value of a given type into a String.

34 / 35

slide-35
SLIDE 35

Exercises

(3) Implement the game of Nim in Haskell, where the rules of the game are as follows: The initial board comprises five rows of stars as follows:

  • 1. * * * * *
  • 2. * * * *
  • 3. * * *
  • 4. * *
  • 5. *

Two players take it turn about to remove one or more stars from the end of a single, arbitrary row. The winner is the player who removes the last star or stars from the board. Hint: Represent the board as a list of five integers that give the number of stars remaining on each row. For example, the initial board is [5,4,3,2,1]. Hint: You can transform a String into an Int using read. Hint: Make sure you clearly separate between functionality which needs IO and which can be implemented as pure functions.

35 / 35