View
212
Download
0
Embed Size (px)
Citation preview
Using Haskell: The Hugs Interpreter
A module isloaded.
Type anexpression atthe prompt.The value
is printed.
Using Haskell: The Hugs Interpreter
A functioncall with twoarguments.
No brackets!
Brackets areonly for grouping
e.g. f (g x)
Defining Functions
insert :: Ord a => a -> [a] -> [a]insert x [] = []insert x (y:xs) | x<=y = x:y:xs
| x>y = y:insert x xs
Defining Functions
insert :: Ord a => a -> [a] -> [a]insert x [] = []insert x (y:xs) | x<=y = x:y:xs
| x>y = y:insert x xs
Type signature.Optional!
Defining Functions
insert :: Ord a => a -> [a] -> [a]insert x [] = []insert x (y:xs) | x<=y = x:y:xs
| x>y = y:insert x xs
Type signature.Optional!
Ignore for now
Defining Functions
insert :: Ord a => a -> [a] -> [a]insert x [] = []insert x (y:xs) | x<=y = x:y:xs
| x>y = y:insert x xs
Type signature.Optional!
Ignore for now Type of firstargument: “a”
Defining Functions
insert :: Ord a => a -> [a] -> [a]insert x [] = []insert x (y:xs) | x<=y = x:y:xs
| x>y = y:insert x xs
Type signature.Optional!
Ignore for now Type of firstargument: “a”
Type of secondargument:list of “a”s
Defining Functions
insert :: Ord a => a -> [a] -> [a]insert x [] = []insert x (y:xs) | x<=y = x:y:xs
| x>y = y:insert x xs
Type signature.Optional!
Ignore for now Type of firstargument: “a”
Type of secondargument:list of “a”s
Type ofresult
Defining Functions
insert :: Ord a => a -> [a] -> [a]insert x [] = []insert x (y:xs) | x<=y = x:y:xs
| x>y = y:insert x xs
Type signature.Optional!
Ignore for now Type of firstargument: “a”
Type of secondargument:list of “a”s
Type ofresult
What is “a”? A type variable which can stand for any type.
This function is polymorphic.
Defining Functions
insert :: Ord a => a -> [a] -> [a]insert x [] = []insert x (y:xs) | x<=y = x:y:xs
| x>y = y:insert x xs
Definition by “patternmatching”: case
analysis on the formof the arguments.
Defining Functions
insert :: Ord a => a -> [a] -> [a]insert x [] = []insert x (y:xs) | x<=y = x:y:xs
| x>y = y:insert x xs
Definition by “patternmatching”: case
analysis on the formof the arguments.
“Guards” defineconditions for anequation to apply.
Defining Functions
insert :: Ord a => a -> [a] -> [a]insert x [] = []insert x (y:xs) | x<=y = x:y:xs
| x>y = y:insert x xs
Definition by “patternmatching”: case
analysis on the formof the arguments.
“Guards” defineconditions for anequation to apply.
We build anew structureas the result:
“purelyfunctional”.
Defining Data Types
data Tree a = Node a (Tree a) (Tree a) | Leaf deriving Show
Type name Type parameter
•Types may take parameters.
•Enables us to define polymorphic functions which work on a tree with any type of labels.
Defining Data Types
data Tree a = Node a (Tree a) (Tree a) | Leaf deriving Show
Type name Type parameter
•Types may take parameters.
•Enables us to define polymorphic functions which work on a tree with any type of labels.
Constants startwith upper case,
variables with lower
Defining Data Types
data Tree a = Node a (Tree a) (Tree a) | Leaf deriving Show
Node and Leaf arealternative forms of Tree.
Defining Data Types
data Tree a = Node a (Tree a) (Tree a) | Leaf deriving Show
Node and Leaf arealternative forms of Tree.
Types of thecomponents.
Defining Data Types
data Tree a = Node a (Tree a) (Tree a) | Leaf deriving Show
Node and Leaf arealternative forms of Tree.
Types of thecomponents.
Ignore for now.
Tree Insertion
insertTree :: Ord a => a -> Tree a -> Tree ainsertTree x Leaf = Node x Leaf LeafinsertTree x (Node y l r) | x < y = Node y (insertTree x l) r | x > y = Node y l (insertTree x r) | x==y = Node y l r
Patternmatchingworks asfor lists.
Overloading•Polymorphic functions use the same definition at each type.
•Overloaded functions may have a different definition at each type.
class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool x/=y = not (x==y)
Class name.
Classmethods
and types.
Default definition.
Read:
“a is a type in class Eq, if it has the following methods”.
The Class Hierarchy
class Eq a => Ord a where (<) :: a -> a -> Bool …
Read:
“Type a in class Eq is also in class Ord, if it provides the following methods…”
Instance Declarations
instance Eq Integer where x==y = …primitive…
instance Eq a => Eq [a] where [] == [] = True x:xs == y:ys =
x == y && xs == ys
Provided a is in class Eq, then [a] is in class Eq, with the method definition given.
Types of Overloaded Functions
insert :: Ord a => a -> [a] -> [a]insert x [] = []insert x (y:xs) | x<=y = x:y:xs
| x>y = y:insert x xs
“a” may be any typein class Ord.
Because insertuses a method
from class Ord.
Show and Read
class Show a where show :: a -> String
class Read a where read :: String -> a
These are simplifications: there are more methods in reality.
read . show = id (usually)
Derived Instances
data Tree a = Node a (Tree a) (Tree a) | Leaf deriving Show
Constructs a “defaultinstance” of class Show.
Main> show (Node 1 Leaf (Node 2 Leaf Leaf))"Node 1 Leaf (Node 2 Leaf Leaf)"
Works for many standard classes.
Multi-Parameter Classes
Define relations between classes.
class Collection c a where empty :: c add :: a -> c -> c member :: a -> c -> Bool
c is a collection with elements of type a.
instance Eq a => Collection [a] a where empty = [] add = (:) member = elem
instance Ord a => Collection (Tree a) a where empty = Leaf add = insertTree member = elemTree
Functional Dependencies
class Collection c a | c -> a where empty :: c add :: a -> c -> c member :: a -> c -> Bool
A functional dependency
•Declares that c determines a: there can be only one instance for each type c.
•Helps the type-checker resolve ambiguities (tremendously).
add x (add y empty) -- x and y must be the same type.
“Side Effects” in Haskell
Suppose
tick :: String -> Integer
•reads an integer n from a file with given name,
•writes n+1 back to the file
•returns n
Then tick == tick might be False!
Cannot replace equals by equals.
Not “purely functional”!
Haskell’s SolutionSide effects are recorded in the type!
readFile :: String -> IO StringwriteFile :: String -> String -> IO ()
So the type of tick is
tick :: String -> IO Int
and tick == tick is ill-typed.
Performs I/Oand delivers
a String.
IO is a monad --more later!
The do notation
I/O actions may be combined in sequence.
tick :: String -> IO Integertick f = do contents <- readFile f
let n = read contentswriteFile f (show (n+1))return n
The do notation
I/O actions may be combined in sequence.
tick :: String -> IO Integertick f = do contents <- readFile f
let n = read contentswriteFile f (show (n+1))return n
Type: IO String
Type: String Scope ofcontents
The do notation
I/O actions may be combined in sequence.
tick :: String -> IO Integertick f = do contents <- readFile f
let n = read contentswriteFile f (show (n+1))return n
Localdeclaration
Scope
The do notation
I/O actions may be combined in sequence.
tick :: String -> IO Integertick f = do contents <- readFile f
let n = read contentswriteFile f (show (n+1))return n
Type: IO ()
No need tobind a nameto the result.
The do notation
I/O actions may be combined in sequence.
tick :: String -> IO Integertick f = do contents <- readFile f
let n = read contentswriteFile f (show (n+1))return n
Type: IO IntegerDefines result of tick.
return :: a -> IO a
IO a = Action Yeilding an a
Action at theprompt is performed.
The actionreturned by tickhas a side-effect.
Yields a differentInteger each timeit is performed.
IO a = Action Yeilding an a
twice1 :: IO a -> (IO a, IO a)twice1 c = (c,c)
Result is not an action!Therefore not performed.
Next call reveals noside-effects occurred.
IO a = Action Yeilding an atwice2 :: IO a -> IO (a,a)twice2 a = do x <- a
return (x,x)
twice3 :: IO a -> IO (a,a)twice3 a = do x <- a
y <- areturn (x,y)
The same action can beperformed many times.
References
Variables in Haskell cannot be updated -- references can.
newIORef :: a -> IO (IORef a)readIORef :: IORef a -> IO awriteIORef :: IORef a -> a -> IO ()
Reference operations have side-effects -- hence IO type.
Example: Destructive List Insertion
data RList a = Nil | Cons a (IORef (RList a))
insertRList :: Ord a => a -> IORef (RList a) -> IO ()insertRList x xs = do cell <- readIORef xs case cell of
Nil -> do new <- newIORef Nil writeIORef xs (Cons x new) Cons y xs' | x<=y -> do new <- newIORef cell
writeIORef xs (Cons x new) | x>y -> insertRList x xs'
Updateabletail.
Must read the list cell.
case isinline
patternmatching.
Create newcell and
update old.
Encapsulated Side Effects
•IORefs can only be updated at the top level.
•Can we use references internally to define a pure function?
Example
removeDuplicates :: Hashable a => [a] -> [a]
Use a hash tableinternally to makecomparison fast.
No IO type:no externally
visible side-effects!
Array operationsresemble reference ones.
Encapsulation: The ST Monad
newSTRef :: a -> ST s (STRef s a)readSTRef :: STRef s a -> ST s awriteSTRef :: STRef s a -> a -> ST s ()
Similar family of operations.
Encapsulation: The ST Monad
newSTRef :: a -> ST s (STRef s a)readSTRef :: STRef s a -> ST s awriteSTRef :: STRef s a -> a -> ST s ()
s ties together the typeof a reference and theaction which uses it.
Encapsulation: The ST Monad
newSTRef :: a -> ST s (STRef s a)readSTRef :: STRef s a -> ST s awriteSTRef :: STRef s a -> a -> ST s ()
The encapsulation function:
runST :: (forall s . ST s a) -> a
Encapsulation: The ST Monad
newSTRef :: a -> ST s (STRef s a)readSTRef :: STRef s a -> ST s awriteSTRef :: STRef s a -> a -> ST s ()
The encapsulation function:
runST :: (forall s . ST s a) -> a
Result typefree from ST:
“pure”.
Encapsulation: The ST Monad
newSTRef :: a -> ST s (STRef s a)readSTRef :: STRef s a -> ST s awriteSTRef :: STRef s a -> a -> ST s ()
The encapsulation function:
runST :: (forall s . ST s a) -> a
Result typefree from ST:
“pure”.
Each use binds a freshvariable s: labels references
created, guarantees usedonly here.
Encapsulation: The ST Monad
newSTRef :: a -> ST s (STRef s a)readSTRef :: STRef s a -> ST s awriteSTRef :: STRef s a -> a -> ST s ()
The encapsulation function:
runST :: (forall s . ST s a) -> a
The argument of runSTmust be polymorphic in s.
This is a “rank 2” type.Cannot be inferred --
must be declared.
Overloading Side-Effects
Why should we choose between IO and ST when we want side-effects?
class Monad m => RefMonad m r | m -> r where newRef :: a -> m (r a) readRef :: r a -> m a writeRef :: r a -> a -> m ()
instance RefMonad IO IORef …
instance RefMonad (ST s) (STRef s) …
Overloading Side-Effects
Why should we choose between IO and ST when we want side-effects?
class Monad m => RefMonad m r | m -> r where newRef :: a -> m (r a) readRef :: r a -> m a writeRef :: r a -> a -> m ()
instance RefMonad IO IORef …
instance RefMonad (ST s) (STRef s) …
Partial type application.Plug in ST s and STRef s for m and r in the types…
Overloading Side-Effects
Why should we choose between IO and ST when we want side-effects?
class Monad m => RefMonad m r | m -> r where newRef :: a -> ST s (STRef s a) readRef :: STRef s a -> ST s a writeRef :: STRef s a -> a -> ST s ()
instance RefMonad IO IORef …
instance RefMonad (ST s) (STRef s) …
Overloading Side-Effects
Why should we choose between IO and ST when we want side-effects?
class Monad m => RefMonad m r | m -> r where newRef :: a -> m (r a) readRef :: r a -> m a writeRef :: r a -> a -> m ()
Example
data RList r a = Nil | Cons a (r (RList r a))insertRList :: (Ord a, RefMonad m r) =>
a -> r (RList r a) -> m ()
Higher-Order Functions
•Functions are values in Haskell.
•“Program skeletons” take functions as parameters.
takeWhile :: (a -> Bool) -> [a] -> [a]takeWhile p [] = []takeWhile p (x:xs) | p x = x:takeWhile p xs | otherwise = []
Takes a prefix of a list, satisfying a predicate.
Denoting Functions
below a b = b < a
takeWhile :: (a -> Bool) -> [a] -> [a]takeWhile p [] = []takeWhile p (x:xs) | p x = x:takeWhile p xs | otherwise = []
Denoting Functions
below a b = b < a
takeWhile :: (a -> Bool) -> [a] -> [a]takeWhile p [] = []takeWhile p (x:xs) | below 10 x= x:takeWhile p xs | otherwise = []
A partial function application.
More Ways to Denote Functions
•takeWhile (below 10) [1,5,9,15,20]
•takeWhile (\b -> b < 10) [1,5,9,15,20]
below a b = b < a
“Lambda” expression.Function definition
in place.
More Ways to Denote Functions
•takeWhile (below 10) [1,5,9,15,20]
•takeWhile (\b -> b < 10) [1,5,9,15,20]
below a b = b < a
“Lambda” expression.Function definition
in place.f b = b < 10
More Ways to Denote Functions
•takeWhile (below 10) [1,5,9,15,20]
•takeWhile (\b -> b < 10) [1,5,9,15,20]
•takeWhile (<10) [1,5,9,15,20]
below a b = b < a
“Lambda” expression.Function definition
in place.f b = b < 10
Partial operatorapplication -- argument
replaces missing operand.
Lazy Evaluation
•Expressions are evaluated only when their value is really needed!
•Function arguments, data structure components, are held unevaluated until their value is used.
from n = n : from (n+1)
A Confession
This program doesn’t work!
tick :: String -> IO Integertick f = do contents <- readFile f
let n = read contentswriteFile f (show (n+1))return n
A Confession
This program doesn’t work!
tick :: String -> IO Integertick f = do contents <- readFile f
let n = read contentswriteFile f (show (n+1))return n
The file is onlyopened here, itis read when
contents is needed.
A Confession
This program doesn’t work!
tick :: String -> IO Integertick f = do contents <- readFile f
let n = read contentswriteFile f (show (n+1))return n
The file is onlyopened here, itis read when
contents is needed.
Not needed yet!n isn’t needed…
A Confession
This program doesn’t work!
tick :: String -> IO Integertick f = do contents <- readFile f
let n = read contentswriteFile f (show (n+1))return n
The file is onlyopened here, itis read when
contents is needed.
Not needed yet!n isn’t needed…
Not needed untilafter the file is
opened for writing!
A Confession
This program doesn’t work!
tick :: String -> IO Integertick f = do contents <- readFile f
let n = read contentswriteFile f (show (n+1))return n
The file is onlyopened here, itis read when
contents is needed.
Not needed yet!n isn’t needed…
Not needed untilafter the file is
opened for writing!So readFile sees an empty file!
A Confession
This program doesn’t work!
tick :: String -> IO Integertick f = do contents <- readFile f
let n = read contentsn `seq` writeFile f (show (n+1))return n
A Confession
This program doesn’t work!
tick :: String -> IO Integertick f = do contents <- readFile f
let n = read contents n `seq` writeFile f (show (n+1))return n
`seq` evaluatesits first argument,
then returns itssecond.
A Confession
This program doesn’t work!
tick :: String -> IO Integertick f = do contents <- readFile f
let n = read contents n `seq` writeFile f (show (n+1))return n
`seq` evaluatesits first argument,
then returns itssecond.
Backquotes `…`turn a functionname into an
operator.