a lightning tour of haskell lecture 1, designing and using combinators john hughes

80
A Lightning Tour of Haskell Lecture 1, Designing and Using Combinators John Hughes

Post on 20-Dec-2015

212 views

Category:

Documents


0 download

TRANSCRIPT

A Lightning Tour of Haskell

Lecture 1,

Designing and Using Combinators

John Hughes

Using Haskell: The Hugs Interpreter

Using Haskell: The Hugs Interpreter

A module isloaded.

Using Haskell: The Hugs Interpreter

A module isloaded.

Type anexpression atthe prompt.

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)

Using Haskell: The Hugs Interpreter

A linked list

1

3

5

1 : 3 : 5 : []

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

Defining Data Types

data Tree a = Node a (Tree a) (Tree a) | Leaf deriving Show

Type name

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.

Defining Data Types

data Tree a = Node a (Tree a) (Tree a) | Leaf deriving Show

Defining Data Types

data Tree a = Node a (Tree a) (Tree a) | Leaf deriving Show

Type:Tree Integer

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

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

Denoting Functions

below a b = b < a

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] below a b = b < a

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)

Lazy Recursive Definitions

nats = 0 : map (+1) nats

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.

Time for a Demo…

Course Home Page

www.cs.chalmers.se/~rjmh/Combinators

Begin with some “finger exercises” in Haskell.