Lecture No. 10 & 11 Type declarations Data declarations Recursive types Arithmetic expressions Binary trees Type inference I/O
Note The slides are from: Programming in Haskell, Graham Hutton, Cambridge University Press (January 15, 2007)
Type inference Type signatures are not mandatory. Haskell has a type inference system Type inference in Haskell is decidable isL c = c == 'l' This function takes a character and sees if it is an 'l' character.
Type inference isL c = c == 'l' The compiler derives the type for isL something like the following (==) :: a -> a -> Bool 'l' :: Char Replacing the second ''a'' in the signature for (==) with the type of 'l': (==) :: Char -> Char -> Bool isL :: Char -> Bool the return value from the call to (==) becomes the return value of isL function.
Type inference isL is a function which takes a single argument. We discovered that this argument must be of type Char. Finally, we derived that we return a Bool. So, we can confidently say that isL has the type: isL :: Char -> Bool isL c = c == 'l'
Reasons to use type signatures Documentation: the most prominent reason is that it makes your code easier to read. Debugging: if you annotate a function with a type, then make a typo in the body of the function, the compiler will tell you at compile-time that your function is wrong. Types prevent errors
Reasons to use type signatures fiveOrSix :: Bool -> Int fiveOrSix True = 5 fiveOrSix False = 6 pairToInt :: (Bool, String) -> Int pairToInt x = fiveOrSix (fst x) The function fiveOrSix takes a Bool. When pairToInt receives its arguments, it knows, because of the type signature that the first element of the pair is a Bool. Extract this using fst and pass that into fiveOrSix This would work, because the type of the first element of the pair and the type of the argument to fiveOrSix are the same.
I/O Performing input/output in a purely functional language like Haskell has long been a fundamental problem. How to implement operations like getChar which returns the latest character that the user has typed or putChar c which prints the character c on the screen? We somehow have to capture that getChar also performs the side effect of interacting with the user.
I/O Haskell's I/O system is built around a mathematical foundation: the monad. Monads are a conceptual structure into which I/O happens to fit. I/O operations are seen as actions Actions are defined rather than invoked within the expression language of Haskell.
I/O actions The invocation of actions takes place outside of the expression evaluation Actions are either atomic, as defined in system primitives, or are a sequential composition of other actions. The I/O monad contains primitives which build composite actions, a process similar to joining statements in sequential order using ";" in other languages. Thus the monad serves as the glue which binds together the actions in a program.
I/O actions Every I/O action returns a value. In the type system, the return value is "tagged" with IO type, distinguishing actions from other values. The type of the function getChar is: getChar :: IO Char The IO Char indicates that getChar, when invoked, performs some action which returns a character.
I/O actions Actions which return no interesting values use the unit type, ( ). For example, the putChar function: putChar :: Char -> IO () takes a character as an argument but returns nothing useful. The unit type is similar to void in other languages.
do Actions are sequenced using the operator >>= (or `bind'). Instead of using this operator directly we can use the do notation The keyword do introduces a sequence of statements which are executed in order. A statement is: an action a pattern bound to the result of an action using <- a set of local definitions introduced using let.
do A simple program to read and then print a character: main :: IO () main = do c <- getChar putChar c
do We can invoke actions and examine their results using do, but how do we return a value from a sequence of actions? Consider the ready function that reads a character and returns True if the character was a `y' ready :: IO Bool ready = do c <- getChar c == 'y' – Not correct !!!
return This doesn't work because the second statement in the 'do' is just a boolean value, not an action. We need to take this boolean and create an action that does nothing but return the boolean as its result. The return function does just that: return :: a -> IO a ready :: IO Bool ready = do c <- getChar return (c == 'y')
Examples mypair :: IO (Char, Char) mypair = do x <- getChar y <- getChar return (x,y)
Examples Reads a string from the keyboard getline :: IO String getline = do x <- getChar if x == '\n' then return [ ] else do xs <- getLine return (x:xs)
Examples 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"
Examples doGuessing num = do putStrLn "Enter your guess:" guess <- getLine if (read guess) < num then do putStrLn "Too low!" doGuessing num else if (read guess) > num then do putStrLn "Too high!" doGuessing num else do putStrLn "You Win!"