Transcript Lect_10_11
Functional Programming
Universitatea Politehnica Bucuresti
2008-2009
Adina Magda Florea
http://turing.cs.pub.ro/fp_09
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
fiveOrSix
fiveOrSix
:: Bool -> Int
True = 5
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
main
:: IO ()
= 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
ready
:: IO Bool
= do c <- getChar
return (c == 'y')
Examples
mypair
mypair
:: IO (Char, Char)
= do x <- getChar
y <- getChar
return (x,y)
Examples
Reads a string from the keyboard
getline
getline
:: IO String
= 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
strlen
:: IO ()
= 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!"