Successfully reported this slideshow.
Upcoming SlideShare
×

# Real World Haskell: Lecture 4

2,204 views

Published on

Published in: Education, Technology
• Full Name
Comment goes here.

Are you sure you want to Yes No
• Be the first to comment

### Real World Haskell: Lecture 4

1. 1. Real World Haskell: Lecture 4 Bryan O’Sullivan 2009-10-29
2. 2. Quaternions A quaternion is a mathematical structure, “the extension of complex numbers to three dimensions.” In mathematical notation, some people write a quaternion q as follows: q = a + bi + cj + dk The i, j, and k terms (the basis elements) are related to the ι of complex numbers, and have similarly weird properties: i2 = j2 = k2 = ijk = −1
3. 3. What are quaternions for? Invented in 1843 by William Rowan Hamilton. Brieﬂy used in 19th-century physics, e.g. in the description of Maxwell’s equations that related electric and magnetic ﬁelds to charge density and current density. Lost favour in the 1890s, supplanted by vector analysis. Recently popular again, due to utility in describing rotations. Current uses include: Computer graphics (smoothly interpolated rotations) Signal processing (image disparity, e.g. in geophysics) Attitude control (e.g. in spacecraft) Orbital mechanics Simulation of molecular dynamics
4. 4. What’s this got to do with Haskell? The set H of quaternions is equal to R4 (a 4d vector space over the reals), so we could conceivably use a representation like this: type Q u a t e r n i o n = ( Double , Double , Double , Double ) Quaternions are not arbitrary 4-vectors; they have special properties. So why might we not want to use this deﬁnition? Since Quaternion is simply a synonym for a 4-tuple of Doubles, the compiler will treat the two types as the same, even when our intended meaning is distinct.
5. 5. Giving types more structure We conclude that we want a quaternion to somehow “look diﬀerent” from a 4-tuple. Here’s how we can do it: data Q u a t e r n i o n = Q Double Double Double Double The data directive introduces a completely new type named Quaternion. We call the name that follows the data directive the type constructor: it identiﬁes the type.
6. 6. Data constructors data Q u a t e r n i o n = Q Double Double Double Double Following the “=” sign above is the name Q, which is a data constructor for the Quaternion type. We use a data constructor to build a new value of a given type. Type constructors create types. Data constructors create values.
7. 7. Using constructors (I) The 4 instances of Double following the Q are the type parameters to that data constructor. When we use our data constructor, we need to supply values: The number of values must match the number of type parameters. The type of each value must match the type parameter at that position. We have a simple case here: just plug in four numbers. zero = Q 0 0 0 0
8. 8. Using constructors (II) Remember the data constructors for lists? (:) : : a −> [ a ] −> [ a ] [] :: [a]
9. 9. Using constructors (II) Remember the data constructors for lists? (:) : : a −> [ a ] −> [ a ] [] :: [a] We use data constructors to create new values. foo = ’ f ’ : ’o ’ : ’o ’ : [ ]
10. 10. Using constructors (II) Remember the data constructors for lists? (:) : : a −> [ a ] −> [ a ] [] :: [a] We use data constructors to create new values. foo = ’ f ’ : ’o ’ : ’o ’ : [ ] We use type constructors in type signatures. stripLeft : : [ Char ] −> [ Char ]
11. 11. Using constructors (II) Remember the data constructors for lists? (:) : : a −> [ a ] −> [ a ] [] :: [a] We use data constructors to create new values. foo = ’ f ’ : ’o ’ : ’o ’ : [ ] We use type constructors in type signatures. stripLeft : : [ Char ] −> [ Char ] We pattern match against data constructors to inspect a value. s t r i p L e f t ( x : xs ) | isSpace x = s t r i p L e f t xs | otherwise = x : xs stripLeft [] = []
12. 12. Using data constructors (III) We do the same with our new Quaternion type. Here’s a type signature for the function that creates a scalar-valued quaternion. Notice the use of the type constructor. s c a l a r : : Double −> Q u a t e r n i o n And here’s the deﬁnition of the scalar function, where we use the data constructor to create a new value of type Quaternion. scalar s = Q s 0 0 0
13. 13. Using data constructors (IV) We perform pattern matching as you might expect: s c a l a r P a r t : : Q u a t e r n i o n −> Double scalarPart (Q s ) = s The special pattern “ ” above has the following meaning: “I don’t care what’s at this position. Don’t inspect the value here, and don’t bind it to a name.” In other words, it’s a wild card.
14. 14. Using data constructors (IV) We perform pattern matching as you might expect: s c a l a r P a r t : : Q u a t e r n i o n −> Double scalarPart (Q s ) = s The special pattern “ ” above has the following meaning: “I don’t care what’s at this position. Don’t inspect the value here, and don’t bind it to a name.” In other words, it’s a wild card. Notice that we had to provide four patterns after the Q, one to match each of the Q constructor’s parameters.
15. 15. Spelunking with the interpreter We can usefully inspect our new type in ghci: Prelude> :info Quaternion data Quaternion = Q Double Double Double Double -- Defined at Quaternion.hs:1:5-14 The :info command is very useful! It tells us everything ghci knows about an identiﬁer (regardless of whether it’s a type, a function, or whatever).
16. 16. Spelunking with the interpreter We can usefully inspect our new type in ghci: Prelude> :info Quaternion data Quaternion = Q Double Double Double Double -- Defined at Quaternion.hs:1:5-14 The :info command is very useful! It tells us everything ghci knows about an identiﬁer (regardless of whether it’s a type, a function, or whatever). We can also inspect the type of the Q data constructor: Prelude> :type Q Q :: Double -> Double -> Double -> Double -> Quaternion
17. 17. What does this correspond to? Are you struggling to follow so far?
18. 18. What does this correspond to? Are you struggling to follow so far? Here’s an approximate equivalent to what we just covered, in a familiar language: struct quaternion { double a, b, c, d; };
19. 19. What does this correspond to? Are you struggling to follow so far? Here’s an approximate equivalent to what we just covered, in a familiar language: struct quaternion { double a, b, c, d; }; And here’s another, in a diﬀerent (but still familiar) language: class Quaternion(object): def __init__(self, a,b,c,d): self.a,self.b,self.c,self.d = a,b,c,d
20. 20. Comparing quaternions We’re used to being able to compare values. ( ” f o o ” == ” b a r ” , ” baz ” /= ” quux ” ) == ( False , True ) > So how should we compare quaternions? This deﬁnition won’t compile: w t f = s c a l a r 1 == s c a l a r 2 Ugh, okay. Let’s try this: e q u a l s (Q a b c d ) (Q w x y z ) = a == w && b == x && c == y && d == z
21. 21. Notational aside: functions and operators Functions and operators are the same, save for ﬁxity. We usually apply functions in preﬁx position, and operators in inﬁx position. However, we can treat an operator as a function by wrapping it in parentheses: (+) 1 2 (:) ’a ’ [ ] zipWith (+) [ 1 , 2 , 3 ] [ 4 , 5 , 6 ] We can also apply a function as if it was an operator, by wrapping it in backquotes: s c a l a r 1 ‘ equals ‘ scalar 2 ” wtf ” ‘ isPrefixOf ‘ ” wtfbbq ”
22. 22. But still. . . ugh! It seems lame that we can write this for strings: ” f o o ” == ” b a r ” And, even with our shiny new inﬁx syntax, ﬁnd ourselves stuck writing code like this for quaternions: s c a l a r 2 ‘ equals ‘ s c a l a r 3.14159265 Surely there’s some kind of unifying principle we could put to work?
23. 23. Enter the typeclass Here is a deﬁnition of a widely used typeclass: c l a s s Eq a where (==) : : a −> a −> Bool The deﬁnition says “there is a class of types that support a function named (==).”
24. 24. Enter the typeclass Here is a deﬁnition of a widely used typeclass: c l a s s Eq a where (==) : : a −> a −> Bool The deﬁnition says “there is a class of types that support a function named (==).” How can we declare a type as being a member of this Eq class? i n s t a n c e Eq Q u a t e r n i o n where (==) = e q u a l s This means “any time someone asks for the (==) function on Quaternion type, use the equals function.”
25. 25. Use of (==) As we know, we can use the (==) operator when writing functions: elem a [ ] = False elem a ( x : x s ) | a == x = True | o t h e r w i s e = elem a x s Notice something very important: This function needs to know almost nothing about the type of the values a and x. It’s enough to know that the type supports the (==) function, i.e. that it’s an instance of the Eq typeclass.
26. 26. Bounded polymorphism The elem function is clearly polymorphic, but it’s not fully polymorphic. It will work for any instance of Eq. However, it will not work on types that are not instances of Eq, since they don’t supply an implementation of (==). So our function exhibits a special kind of polymorphism called bounded (or constrained) polymorphism.
27. 27. Expressing bounded polymorphism We can express the fact that elem only accepts instances of Eq in its type signature: elem : : ( Eq a ) = a −> [ a ] −> Bool > The “(Eq a) =>” part of the signature is called a constraint. In the absence of a type signature, a Haskell compiler will infer the presence of any necessary constraints.
28. 28. Expressing bounded polymorphism We can express the fact that elem only accepts instances of Eq in its type signature: elem : : ( Eq a ) = a −> [ a ] −> Bool > The “(Eq a) =>” part of the signature is called a constraint. In the absence of a type signature, a Haskell compiler will infer the presence of any necessary constraints. Aside: Haskell’s kind of polymorphism is called parametric. In this type signature: (++) : : [ a ] −> [ a ] −> [ a ] The type of (++) is parameterized by the type variable a.
29. 29. Rainbows, oh my! Suppose we want to refer to symbolic values. data Rainbow = Red | Orange | Y e l l o w | Green | Blue | I n d i g o | V i o l e t We have deﬁned a type Rainbow with a whole pile of data constructors. These constructors are all independent. If you’re a C or C++ hacker, think of Rainbow as an enumerated type (enum). Each data constructor creates a value of type Rainbow. We can pattern match against a value to see whether it is Green, or Blue, or whatever.
30. 30. Showing your work We haven’t indicated how to print values from our new datatypes yet. Indeed, unless we do something explicit, we can’t print them. Fortunately for us, there’s a Show typeclass, with a useful function: show : : (Show a ) = a −> S t r i n g > So. What do we do? i n s t a n c e Show Rainbow where show Red = ”Red” show Green = ” Green ” {− . . . e t c . . . −}
31. 31. Yeuch! That’s some serious drudgery there. I hate repeating myself. Fortunately, for some key typeclasses, Haskell can automatically derive instances for us as follows. data Rainbow = Red | Orange | Y e l l o w | Green | Blue | I n d i g o | V i o l e t d e r i v i n g ( Eq , Ord , Bounded , Enum, Show) This saves a lot of meaningless ﬁnger-plonkulating.
32. 32. Homework (I)—the trivial part Use ghci to investigate the Ord, Bounded, and Enum typeclasses.
33. 33. Homework (II) Three non-overlapping points in the plane R2 form either an acute angle (< 180◦ ), a straight line, or an obtuse angle (> 180◦ ). Deﬁne a type that represents these possibilities. Write a type signature for a function that accepts three points, and categorises them in this fashion. Now write the function itself.
34. 34. Homework (III) In computational geometry, the convex hull of a set of non-overlapping points is the minimal bounding polygon for which all angles are acute. Using your categorisation function, write a function that computes the convex hull of a set of points. (If you need a hint, look up “Graham’s scan algorithm”.)