real world haskell: lecture 5

24
Real World Haskell: Lecture 5 Bryan O’Sullivan 2009-11-04

Upload: bryan-osullivan

Post on 12-May-2015

1.545 views

Category:

Education


1 download

TRANSCRIPT

Page 1: Real World Haskell: Lecture 5

Real World Haskell:Lecture 5

Bryan O’Sullivan

2009-11-04

Page 2: Real World Haskell: Lecture 5

Adding quaternions

We add two quaternions by adding their coefficients.

a + bi + cj + dk+ w + x i + y j + zk= (a + w) + (b + x)i + (c + y)j + (d + z)k

Or, in Haskell:

addQ (Q a b c d ) (Q w x y z ) =Q ( a+w) ( b+x ) ( c+y ) ( d+z )

Page 3: Real World Haskell: Lecture 5

Subtracting quaternions

Subtraction is defined similarly.

subQ (Q a b c d ) (Q w x y z ) =Q ( a−w) ( b−x ) ( c−y ) ( d−z )

Page 4: Real World Haskell: Lecture 5

Typeclass inheritance

The Eq typeclass that we met last week lets us compare values forequality.

For many values, we want to be able to compare them for (total)ordering, for which we use the Ord typeclass.

c l a s s (Eq a ) => Ord a wherecompare : : a −> a −> Ordering

Notice the constraint on the Ord class: this states that in order fora type to be an instance of Ord, it must already be an instance ofEq.

Page 5: Real World Haskell: Lecture 5

The Num typeclass

Haskell defines a typeclass named Num that lets us expresscommon arithmetic operations:

c l a s s (Eq a , Show a ) => Num a where(+) : : a −> a −> a(∗ ) : : a −> a −> a(−) : : a −> a −> anegate : : a −> aabs : : a −> a{− e t c . −}

Page 6: Real World Haskell: Lecture 5

Num and quaternions

On first glance, we can easily fit our Quaternion type into theNum class.

instance Num Q u a t e r n i o n where(+) = addQ(−) = subQ{− ??? −}

We quickly run into problems: it’s not obvious what negate or absshould do.

Maybe we can do something with multiplication, though?

Page 7: Real World Haskell: Lecture 5

Multiplying quaternions

If we can remember the identities

i2 = j2 = k2 = ijk = −1

Then multiplication of quaternions falls out from the usual laws ofarithmetic, but takes a complicated form:

a + bi + cj + dk ∗ w + x i + y j + zk = aw − bx − cy − dz

+ (ax + bw + cz − dy)i+ (ay − bz + cw + dx)j+ (az + by − cx + dw)k

Page 8: Real World Haskell: Lecture 5

Multiplying quaternions in Haskell

It’s easy to convert our equation into executable form:

mulQ (Q a b c d ) (Q w x y z )= Q ( a∗w − b∗x − c∗y − d∗ z )

( a∗x + b∗w + c∗ z − d∗y )( a∗y − b∗ z + c∗w + d∗x )( a∗ z + b∗y − c∗x + d∗w)

Does this mean that we should augment our definition of Num asfollows?

instance Num Q u a t e r n i o n where(∗ ) = mulQ{− e t c . −}

Page 9: Real World Haskell: Lecture 5

Multiplying quaternions in Haskell

It’s easy to convert our equation into executable form:

mulQ (Q a b c d ) (Q w x y z )= Q ( a∗w − b∗x − c∗y − d∗ z )

( a∗x + b∗w + c∗ z − d∗y )( a∗y − b∗ z + c∗w + d∗x )( a∗ z + b∗y − c∗x + d∗w)

Does this mean that we should augment our definition of Num asfollows?

instance Num Q u a t e r n i o n where(∗ ) = mulQ{− e t c . −}

Page 10: Real World Haskell: Lecture 5

Arithmetic laws

There are some laws that are so ingrained into our minds that wenever think about them:

m + n = n + m (commutative law of addition)

(m + n) + k = m + (n + k) (associative law of addition)

mn = nm (commutative law of multiplication)

(mn)k = m(nk) (associative law of multiplication)

Page 11: Real World Haskell: Lecture 5

Laws for quaternions

We can see by simple inspection that addition over quaternionsmust satisfy the commutative and associative laws of normalarithmetic.

We used those familiar arithmetic laws to derive the formula forquaternion multiplication, but do quaternions satisfy thecommutative law of multiplication?

Prelude> let a = Q 2 0 9 0Prelude> let b = Q 0 9 0 2

Prelude> a ‘mulQ‘ bQ 0.0 36.0 0.0 (-77.0)

Prelude> b ‘mulQ‘ aQ 0.0 0.0 0.0 85.0

Page 12: Real World Haskell: Lecture 5

Laws: made to be broken?

When you write or use a typeclass, there’s an impliedunderstanding that you’ll obey its laws1.

Code that uses Eq relies on the fact that if a == b is True, thenb == a will be True too, and a /= b will be False.

Similarly, code that uses Num implicitly relies on thecommutativity and associativity of addition and multiplication.

Neither the type system nor any other aspect of Haskell will helpyou to do the heavy lifting here:

I The burden is on you, the creator of a type, to ensure that ifyou make it an instance of a typeclass, that it follows the laws.

1Unfortunately, these laws are often undocumented.

Page 13: Real World Haskell: Lecture 5

Laws: made to be broken?

When you write or use a typeclass, there’s an impliedunderstanding that you’ll obey its laws1.

Code that uses Eq relies on the fact that if a == b is True, thenb == a will be True too, and a /= b will be False.

Similarly, code that uses Num implicitly relies on thecommutativity and associativity of addition and multiplication.

Neither the type system nor any other aspect of Haskell will helpyou to do the heavy lifting here:

I The burden is on you, the creator of a type, to ensure that ifyou make it an instance of a typeclass, that it follows the laws.

1Unfortunately, these laws are often undocumented.

Page 14: Real World Haskell: Lecture 5

A sketchy approach

Since quaternion multiplication is not commutative, we should notimplement (∗). But what more should we do?

For instance, we could partially implement Num:

instance Num Q u a t e r n i o n where(+) = addQ(∗ ) = undefined

What effect does this have on code that tries to use multiplication?

Prelude> scalar 2 * scalar 3*** Exception: Prelude.undefined

This is not very satisfactory behaviour.

Page 15: Real World Haskell: Lecture 5

Playing fast and loose

Of course, Haskell itself doesn’t escape the sin bin. What happensto those fancy laws in the presence of inexact arithmetic?

Prelude> let a = 1e20 :: DoublePrelude> (a + (-a)) + 11.0Prelude> a + ((-a) + 1)0.0

(This is the same behaviour as every other language thatimplements floating point, by the way.)

A conclusion? You can violate the rules, but the compiler can’tremind you that you’re cheating.

Page 16: Real World Haskell: Lecture 5

Code that might fail

You’ve probably seen this behaviour by now:

Prelude> 1 ‘div‘ 0*** Exception: divide by zero

These exceptions are often annoying, because we can’t easily catchand handle them.

There exists a predefined type we can use to deal with these cases:

data Maybe a = Nothing| Just a

Notice that this type is parameterized, so we can have types suchas Maybe Int, or Maybe (String, Bool), or so on.

Page 17: Real World Haskell: Lecture 5

Safer functions via Maybe

Safer integer division:

a ‘ s a f e D i v ‘ 0 = Nothinga ‘ s a f e D i v ‘ b = Just ( a ‘ div ‘ b )

A safer version of head:

sa feHead [ ] = Nothingsa feHead ( x : ) = Just x

Page 18: Real World Haskell: Lecture 5

Exercise time!

You should be familiar with the map function by now:

map : : ( a −> b ) −> [ a ] −> [ b ]

Write the equivalent function for the Maybe type:

mapMaybe : : ( a −> b ) −> Maybe a −> Maybe b

Page 19: Real World Haskell: Lecture 5

Binary trees

data Tree a = Empty| Node a ( Tree a ) ( Tree a )

de r i v i ng (Eq , Ord , Show)

l e a f v = Node v Empty Empty

someOldTree =Node ( l e a f ” f o o ” )

( Node ( l e a f ” bar ” )( Node ( l e a f ” baz ” ) Empty ) )

Page 20: Real World Haskell: Lecture 5

Sizing a tree

s i z e Empty = 0s i z e Node a b = 1 + s i z e a + s i z e b

Page 21: Real World Haskell: Lecture 5

Mapping again

What should this function look like?

mapTree : : ( a −> b ) −> Tree a −> Tree b

Page 22: Real World Haskell: Lecture 5

Generalising mapping

So far, we’ve seen three different container types, with threedifferent map-like functions:

map : : ( a −> b ) −> [ a ] −> [ b ]mapMaybe : : ( a −> b ) −> Maybe a −> Maybe bmapTree : : ( a −> b ) −> Tree a −> Tree b

It turns out we can write a typeclass to generalise this idea:

c l a s s Functor f wherefmap : : ( a −> b ) −> f a −> f b

instance Functor Maybe wherefmap = mapMaybe

Page 23: Real World Haskell: Lecture 5

Homework—binary search trees

Turn the Tree type into a binary search tree by defining thefollowing functions:

i n s e r t : : (Ord a ) => a −> Tree a −> Tree ac o n t a i n s : : (Ord a ) => a −> Tree a −> Bool

Page 24: Real World Haskell: Lecture 5

Homework—key/value containers

Adapt your binary search tree code for use to create a simplekey/value container:

type Map a b = Tree ( a , b )i n s e r t I t e m

: : (Ord a ) => a −> b −> Map a b −> Map a blookupByKey

: : (Ord a ) => a −> Map a b −> Maybe bl i s tToMap

: : (Ord a ) => [ ( a , b ) ] −> Map a bmapToList

: : Map a b −> [ ( a , b ) ]minItem

: : (Ord a ) => Map a b −> Maybe ( a , b )