programming in haskell

47
1 PROGRAMMING IN HASKELL Based on lecture notes by Graham Hutton The book “Learn You a Haskell for Great Goo (and a few other sources) Modules

Upload: rendor

Post on 29-Jan-2016

49 views

Category:

Documents


3 download

DESCRIPTION

PROGRAMMING IN HASKELL. Modules. Based on lecture notes by Graham Hutton The book “ Learn You a Haskell for Great Good ” (and a few other sources). Type Declarations. In Haskell, a new name for an existing type can be defined using a type declaration. type String = [Char]. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: PROGRAMMING IN HASKELL

1

PROGRAMMING IN HASKELL

Based on lecture notes by Graham HuttonThe book “Learn You a Haskell for Great Good”

(and a few other sources)

Modules

Page 2: PROGRAMMING IN HASKELL

2

Type Declarations

In Haskell, a new name for an existing type can be defined using a type declaration.

type String = [Char]

String is a synonym for the type [Char].

Page 3: PROGRAMMING IN HASKELL

3

Type declarations can be used to make other types easier to read. For example, given

origin :: Posorigin = (0,0)

left :: Pos Posleft (x,y) = (x-1,y)

type Pos = (Int,Int)

we can define:

Page 4: PROGRAMMING IN HASKELL

4

Like function definitions, type declarations can also have parameters. For example, given

type Pair a = (a,a)

we can define:

mult :: Pair Int Intmult (m,n) = m*n

copy :: a Pair acopy x = (x,x)

Page 5: PROGRAMMING IN HASKELL

5

Type declarations can be nested:

type Pos = (Int,Int)

type Trans = Pos Pos

However, they cannot be recursive:

type Tree = (Int,[Tree])

Page 6: PROGRAMMING IN HASKELL

6

Data Declarations

A completely new type can be defined by specifying its values using a data declaration.

data Bool = False | True

Bool is a new type, with two new values False

and True.

Page 7: PROGRAMMING IN HASKELL

7

Note:

The two values False and True are called the constructors for the type Bool.

Type and constructor names must begin with an upper-case letter.

Data declarations are similar to context free grammars. The former specifies the values of a type, the latter the sentences of a language.

Page 8: PROGRAMMING IN HASKELL

8

answers :: [Answer]answers = [Yes,No,Unknown]

flip :: Answer Answerflip Yes = Noflip No = Yesflip Unknown = Unknown

data Answer = Yes | No | Unknown

we can define:

Values of new types can be used in the same ways as those of built in types. For example, given

Page 9: PROGRAMMING IN HASKELL

9

The constructors in a data declaration can also have parameters. For example, given

data Shape = Circle Float | Rect Float Float

square :: Float Shapesquare n = Rect n n

area :: Shape Floatarea (Circle r) = pi * r^2area (Rect x y) = x * y

we can define:

Page 10: PROGRAMMING IN HASKELL

10

Note:

Shape has values of the form Circle r where r is a float, and Rect x y where x and y are floats.

Circle and Rect can be viewed as functions that construct values of type Shape:

Circle :: Float Shape

Rect :: Float Float Shape

Page 11: PROGRAMMING IN HASKELL

11

Recursive Types

In Haskell, new types can be declared in terms of themselves. That is, types can be recursive.

data Nat = Zero | Succ Nat

Nat is a new type, with constructors Zero :: Nat and

Succ :: Nat Nat.

Page 12: PROGRAMMING IN HASKELL

12

Note:

A value of type Nat is either Zero, or of the form Succ n where n :: Nat. That is, Nat contains the following infinite sequence of values:

Zero

Succ Zero

Succ (Succ Zero)

Page 13: PROGRAMMING IN HASKELL

13

We can think of values of type Nat as natural numbers, where Zero represents 0, and Succ represents the successor function 1+.

For example, the value

Succ (Succ (Succ Zero))

represents the natural number

1 + (1 + (1 + 0)) 3=

Page 14: PROGRAMMING IN HASKELL

14

Using recursion, it is easy to define functions that convert between values of type Nat and Int:

nat2int :: Nat Int

nat2int Zero = 0

nat2int (Succ n) = 1 + nat2int n

int2nat :: Int Nat

int2nat 0 = Zero

int2nat (n+1) = Succ (int2nat n)

Page 15: PROGRAMMING IN HASKELL

15

Two naturals can be added by converting them to integers, adding, and then converting back:

However, using recursion the function add can be defined without the need for conversions:

add :: Nat Nat Nat

add m n = int2nat (nat2int m + nat2int n)

add Zero n = n

add (Succ m) n = Succ (add m n)

Page 16: PROGRAMMING IN HASKELL

16

For example:

add (Succ (Succ Zero)) (Succ Zero)

Succ (add (Succ Zero) (Succ Zero))=

Succ (Succ (add Zero (Succ Zero))=

Succ (Succ (Succ Zero))=

Note:

The recursive definition for add corresponds to the laws 0+n = n and (1+m)+n = 1+(m+n).

Page 17: PROGRAMMING IN HASKELL

17

Arithmetic Expressions

Consider a simple form of expressions built up from integers using addition and multiplication.

1

+

32

Page 18: PROGRAMMING IN HASKELL

18

Using recursion, a suitable new type to represent such expressions can be declared by:

For example, the expression on the previous slide would be represented as follows:

data Expr = Val Int | Add Expr Expr | Mul Expr Expr

Add (Val 1) (Mul (Val 2) (Val 3))

Page 19: PROGRAMMING IN HASKELL

19

Using recursion, it is now easy to define functions that process expressions. For example:

size :: Expr Int

size (Val n) = 1

size (Add x y) = size x + size y

size (Mul x y) = size x + size y

eval :: Expr Int

eval (Val n) = n

eval (Add x y) = eval x + eval y

eval (Mul x y) = eval x * eval y

Page 20: PROGRAMMING IN HASKELL

20

Note:

The three constructors have types:

Val :: Int ExprAdd :: Expr Expr ExprMul :: Expr Expr Expr

Many functions on expressions can be defined by replacing the constructors by other functions using a suitable fold function. For example:

eval = fold id (+) (*)

Page 21: PROGRAMMING IN HASKELL

21

Binary Trees

In computing, it is often useful to store data in a two-way branching structure or binary tree.

5

7

96

3

41

Page 22: PROGRAMMING IN HASKELL

22

Using recursion, a suitable new type to represent such binary trees can be declared by:

For example, the tree on the previous slide would be represented as follows:

data Tree = Leaf Int | Node Tree Int Tree

Node (Node (Leaf 1) 3 (Leaf 4)) 5 (Node (Leaf 6) 7 (Leaf 9))

Page 23: PROGRAMMING IN HASKELL

23

We can now define a function that decides if a given integer occurs in a binary tree:

occurs :: Int Tree Booloccurs m (Leaf n) = m==noccurs m (Node l n r) = m==n || occurs m l || occurs m r

But… in the worst case, when the integer does not occur, this function traverses the entire tree.

Page 24: PROGRAMMING IN HASKELL

24

Now consider the function flatten that returns the list of all the integers contained in a tree:

flatten :: Tree [Int]flatten (Leaf n) = [n]flatten (Node l n r) = flatten l ++ [n] ++ flatten r

A tree is a search tree if it flattens to a list that is ordered. Our example tree is a search tree, as it flattens to the ordered list [1,3,4,5,6,7,9].

Page 25: PROGRAMMING IN HASKELL

25

Search trees have the important property that when trying to find a value in a tree we can always decide which of the two sub-trees it may occur in:

This new definition is more efficient, because it only traverses one path down the tree.

occurs m (Leaf n) = m==n

occurs m (Node l n r) | m==n = True

| m<n = occurs m l

| m>n = occurs m r

Page 26: PROGRAMMING IN HASKELL

26

Exercise

A binary tree is complete if the two sub-trees of every node are of equal size. Define a function that decides if a binary tree is complete.data Tree = Leaf Int

| Node Tree Int Tree

Node (Node (Leaf 1) 3 (Leaf 4)) 5 (Node (Leaf 6) 7 (Leaf 9))

occurs :: Int Tree Booloccurs m (Leaf n) = m==noccurs m (Node l n r) = m==n || occurs m l || occurs m r

Page 27: PROGRAMMING IN HASKELL

27

ModulesSo far, we’ve been using built-in functions provided in the Haskell prelude. This is a subset of a larger library that is provided with any installation of Haskell. (Google for Hoogle to see a handy search engine for these.)

Examples of other modules: - lists - concurrent programming - complex numbers - char - sets - …

Page 28: PROGRAMMING IN HASKELL

28

Example: Data.List

To load a module, we need to import it:

numUniques :: (Eq a) => [a] -> Int  numUniques = length . nub 

import Data.List 

All the functions in this module are immediately available:

This is a function in Data.List that removes duplicates from a list.

function concatenatio

n

Page 29: PROGRAMMING IN HASKELL

29

You can also load modules from the command prompt:

ghci> :m + Data.List

Or several at once:

import Data.List (nub, sort) import Data.List hiding (nub) 

ghci> :m + Data.List Data.Map Data.Set  

Or import only some, or all but some:

Page 30: PROGRAMMING IN HASKELL

30

If duplication of names is an issue, can extend the namespace:

import qualified Data.Map

When the Data.Map gets a bit long, we can provide an alias:

import qualified Data.Map as M  

And now we can just type M.filter, and the normal list filter will just be filter.

This imports the functions, but we have to use Data.Map to use them – like Data.Map.filter.

Page 31: PROGRAMMING IN HASKELL

31

Data.List has a lot more functionality than we’ve seen. A few examples:

ghci> intersperse '.' "MONKEY"  "M.O.N.K.E.Y"  ghci> intersperse 0 [1,2,3,4,5,6]  [1,0,2,0,3,0,4,0,5,0,6]

ghci> intercalate " " ["hey","there","guys"]  "hey there guys"  ghci> intercalate [0,0,0] [[1,2,3],[4,5,6],

[7,8,9]]  

[1,2,3,0,0,0,4,5,6,0,0,0,7,8,9]   

Page 32: PROGRAMMING IN HASKELL

32

And even more:

ghci> transpose [[1,2,3],[4,5,6],[7,8,9]]  

[[1,4,7],[2,5,8],[3,6,9]]  ghci> transpose ["hey","there","guys"] ["htg","ehu","yey","rs","e"] 

ghci> concat ["foo","bar","car"]  "foobarcar"  ghci> concat [[3,4,5],[2,3,4],[2,1,1]]  [3,4,5,2,3,4,2,1,1]   

Page 33: PROGRAMMING IN HASKELL

33

And even more:

ghci> and $ map (>4) [5,6,7,8]  True  ghci> and $ map (==4) [4,4,4,3,4]  False  

ghci> any (==4) [2,3,5,6,1,4]  True  ghci> all (>4) [6,9,10]  True     

Page 34: PROGRAMMING IN HASKELL

34

A nice example: adding functions

Functions are often represented as vectors: 8x^3 + 5x^2 + x - 1 is [8,5,1,-1].

So we can easily use List functions to add these vectors:

ghci> map sum $ transpose [[0,3,5,9],[10,0,0,9],[8,5,1,-1]]  

[18,8,6,17]

Page 35: PROGRAMMING IN HASKELL

35

There are a ton of these functions, so I could spend all semester covering just lists.

More examples: group, sort, dropWhile, takeWhile, partition, isPrefixOf, find, findIndex, delete, words, insert,…

Instead, I’ll make sure to post a link to a good overview of lists on the webpage, in case you need them.

In essence, if it’s a useful thing to do to a list, Haskell probably supports it!

Page 36: PROGRAMMING IN HASKELL

36

The Data.Char module: includes a lot of useful functions that will look similar to python, actually.

Examples: isAlpha, isLower, isSpace, isDigit, isPunctuation,…

ghci> all isAlphaNum "bobby283"  True  ghci> all isAlphaNum "eddy the fish!"False ghci> groupBy ((==) `on` isSpace) 

"hey guys its me"  ["hey"," ","guys"," ","its"," ","me"]

Page 37: PROGRAMMING IN HASKELL

37

The Data.Char module has a datatype that is a set of comparisons on characters. There is a function called generalCategory that returns the information. (This is a bit like the Ordering type for numbers, which returns LT, EQ, or GT.)ghci> generalCategory ' '  Space  ghci> generalCategory 'A'  UppercaseLetter  ghci> generalCategory 'a'  LowercaseLetter  ghci> generalCategory '.'  OtherPunctuation  ghci> generalCategory '9'  DecimalNumber  ghci> map generalCategory " ¥t¥nA9?|"  [Space,Control,Control,UppercaseLetter,DecimalNumber,OtherPunctuation,MathSymbol]  ]

Page 38: PROGRAMMING IN HASKELL

38

There are also functions that can convert between Ints and Chars:

ghci> map digitToInt "FF85AB"  [15,15,8,5,10,11]ghci> intToDigit 15  'f'  ghci> intToDigit 5  '5'   ghci> chr 97  'a'  ghci> map ord "abcdefgh"  [97,98,99,100,101,102,103,104] 

Page 39: PROGRAMMING IN HASKELL

39

Neat application: Ceasar ciphersA primitive encryption cipher which encodes messages by shifted them a fixed amount in the alphabet.

Example: hello with shift of 3

encode :: Int -> String -> String  encode shift msg =     let ords = map ord msg          shifted = map (+ shift) ords      in  map chr shifted  

Page 40: PROGRAMMING IN HASKELL

40

Now to use it:

ghci> encode 3 "Heeeeey"  "Khhhhh|"  ghci> encode 4 "Heeeeey"  "Liiiii}"  ghci> encode 1 "abcd"  "bcde"  ghci> encode 5 "Marry Christmas! Ho ho ho!”"Rfww~%Hmwnxyrfx&%Mt%mt%mt&"  

Page 41: PROGRAMMING IN HASKELL

41

Decoding just reverses the encoding:

decode :: Int -> String -> String  decode shift msg = 

encode (negate shift) msg    

ghci> encode 3 "Im a little teapot"  "Lp#d#olwwoh#whdsrw"  ghci> decode 3 "Lp#d#olwwoh#whdsrw"  "Im a little teapot"  ghci> decode 5 . encode 5 $ "This is a sentence"  "This is a sentence"     

Page 42: PROGRAMMING IN HASKELL

42

Making our own modules

We specify our own modules at the beginning of a file. For example, if we had a set of geometry functions:

module Geometry  ( sphereVolume  , sphereArea  , cubeVolume  , cubeArea  , cuboidArea  , cuboidVolume  ) where 

Page 43: PROGRAMMING IN HASKELL

43

Then, we put the functions that the module uses:sphereVolume :: Float -> Float  sphereVolume radius = (4.0 / 3.0) * pi * 

(radius ^ 3)  

sphereArea :: Float -> Float  sphereArea radius = 4 * pi * (radius ^ 2)  

cubeVolume :: Float -> Float  cubeVolume side = cuboidVolume side side side …

Page 44: PROGRAMMING IN HASKELL

44

Note that we can have “private” helper functions, also:

cuboidVolume :: Float -> Float -> Float -> Float  

cuboidVolume a b c = rectangleArea a b * c 

cuboidArea :: Float -> Float -> Float -> Float  

cuboidArea a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2  

rectangleArea :: Float -> Float -> Float  rectangleArea a b = a * b 

Page 45: PROGRAMMING IN HASKELL

45

Can also nest these. Make a folder called Geometry, with 3 files inside it:•Sphere.hs•Cubiod.hs•Cube.hsEach will hold a separate group of functions.

To load:

import Geometry.Sphere

Or (if functions have same names):import qualified Geometry.Sphere as Sphere

Page 46: PROGRAMMING IN HASKELL

46

The modules:

module Geometry.Sphere  ( volume  , area  ) where  

volume :: Float -> Float  volume radius = (4.0 / 3.0) * pi * (radius ^ 3)  area :: Float -> Float  area radius = 4 * pi * (radius ^ 2)

Page 47: PROGRAMMING IN HASKELL

47

module Geometry.Cuboid  ( volume  , area  ) where  

volume :: Float -> Float -> Float -> Float  volume a b c = rectangleArea a b * c  

…