Lecture 5

Make Your Own Data Type

- On
**Chrome**: press`F12`

, then click**Console** - On
**IE**: press`F12`

, then click**Console** - On
**Firefox**:`Ctrl+Shift+k`

`↓` , `PgDn` , `n` , `j` |
next slide |

`↑` , `PgUp` , `p` , `k` |
prev slide |

`Esc` |
enables `ctrl+f` globally |

```
-- Designed by Matthew R. Bongiovi
-- using guards
convert :: (Double, [Char]) -> (Double, [Char])
convert (x,l)
| (l == "m") = (1.09361 * x,"yd")
| (l == "L") = (0.264172 * x,"gal")
| (l == "kg") = (2.20462 * x,"lb")
| (l == "yd") = (x / 1.09361, "m")
| (l == "gal") = (x / 0.264172,"L")
| (l == "lb") = (x / 2.20462,"kg")
| otherwise = error "Invalid input"
```

```
-- Designed by James C. Sun
-- using pattern matching
convert :: (Double, [Char]) -> (Double, [Char])
convert (n, "m") = (n*1.09361, "yd")
convert (n, "L") = (n*0.264172, "gal")
convert (n, "kg") = (n*2.20462, "lb")
convert (n, "yd") = (n/1.09361, "m")
convert (n, "gal") = (n/0.264172, "L")
convert (n, "lb") = (n/2.20462, "kg")
convert _ = error "conversion not defined"
```

```
-- Designed by Anthony A. Teate
-- the "I'll do it my own way!" solution
convert :: (Double, [Char]) -> (Double, [Char])
convert(v,s)=head[(a,b)|(a,b)<-(head[[(v*y,z),(v/y,x)]|(x,y,z)<-[("m",1.09361,"yd"),("L",0.264172,"gal"),("kg",2.20462,"lb")],x==s||z==s]),b/=s]
```

```
-- Designed by François Beausoleil
-- the "right way"
module Homework where
data Unit = Meter
| Yard
| Litre
| Gallon
| Kilogram
| Pound
deriving (Ord, Eq)
instance Show Unit where
show Meter = "m"
show Yard = "yd"
show Litre = "l"
show Gallon = "gal"
show Kilogram = "kg"
show Pound = "lb"
factor :: Unit -> Double
factor Meter = 1.09361
factor Litre = 0.264172
factor Kilogram = 2.20462
factor Yard = 1 / (factor Meter)
factor Gallon = 1 / (factor Litre)
factor Pound = 1 / (factor Kilogram)
inverse :: Unit -> Unit
inverse Meter = Yard
inverse Yard = Meter
inverse Litre = Gallon
inverse Gallon = Litre
inverse Kilogram = Pound
inverse Pound = Kilogram
convert :: (Double, Unit) -> (Double, Unit)
convert (n, u) = (n * (factor u), inverse u)
data Measurement = Measurement Double Unit deriving (Eq)
instance Show Measurement where
show (Measurement n u) = (show n) ++ " " ++ (show u)
conv (Measurement n u) = measurement $ convert (n, u)
where measurement (n1, u1) = Measurement n1 u1
```

`Bool`

= False | True`Int`

= -2^{31}... -1 | 0 | 1 ... 2^{31}-1`[Char]`

= [] | Char : [Char]`Double`

- for well structured code
- for better readability
- for improving type safety

`data`

keywordIn a new file `MyData.hs`

create a Data type called `MetricUnits`

```
data MetricUnit = Meter | Liter | KiloGram
```

*MetricUnit* is called the **type constructor**

Everything after the equal sign is called the **value constructor**

Notice the capitalization!

Let's look at *Meter*, *Liter*, and *KiloGram*

```
Prelude> :type True
Bool :: True
Prelude> :load MyData.hs
Prelude> :type Meter
Meter :: MetricUnit
Prelude> :type Liter
Liter :: MetricUnit
Prelude> :type KiloGram
KiloGram :: MetricUnit
```

**Meter** *has the type* **MetricUnit**

`Show`

is a Typeclass that enables data to be shown as a String
```
Prelude> True
True
Prelude> Meter
No instance for (Show MetricUnit) arising from a use of `print'
Possible fix: add an instance declaration for (Show MetricUnit)
```

Why did we get this error?

`deriving (Show)`

GHCi is trying to call the print function

`print`

only works for things that derive Showbut our MetricUnit doesn't derive Show

```
data MetricUnit = Meter | Liter | KiloGram deriving (Show)
```

Haskell programs are type safe.

We can't use our MetricUnit type unless a function accepts it

So let's make our own function

Write a simple function that takes in a MetricUnit, and returns its symbol

- Meter → "m"
- Liter → "L"
- KiloGram → "kg"

```
symbol :: MetricUnit -> String
```

```
symbol :: MetricUnit -> String
symbol Meter = "m"
symbol Liter = "L"
symbol KiloGram = "kg"
```

(clear answer)
(show answer)
Rewrite `symbol`

using guards

```
symbol :: MetricUnit -> String
```

```
symbol :: MetricUnit -> String
symbol x
| x==Meter = "m"
| x==Liter = "L"
| x==KiloGram = "kg"
```

(clear answer)
(show answer)
Try compiling it. What happens?

`deriving (Eq)`

GHCi is yelling at us because we used '=='

```
No instance for (Eq MetricUnit) arising from a use of `=='
Possible fix: add an instance declaration for (Eq MetricUnit)
In the expression: x == Meter
```

So MetricUnit should derive Eq in this case

```
data MetricUnit = Meter | Liter | KiloGram deriving (Show, Eq)
```

Let's write a `convert`

function with our types.

We will convert from Metric to Imperial.

Just like last week's homework

But this time it will be elegant!

1. Define *MetricUnit* and *ImperialUnit*

```
data MetricUnit = Meter
| Liter
| KiloGram
deriving (Show, Eq)
data ImperialUnit = Yard
| Gallon
| Pound
deriving (Show)
```

2. Create a Measurement Data Type

Look closely. This syntax is new!

```
data Measurement = MetricMeasurement Double MetricUnit
| ImperialMeasurement Double ImperialUnit
deriving (Show)
```

`(MetricMeasurement 3.1 Liter) :: Measurement`

`(ImperialMeasurement 200 Pound) :: Measurement`

3. Create the `convert`

function

```
convert :: Measurement -> Measurement
```

Implement it with pattern matching and guards

```
convert (MetricMeasurement x u)
| u==Meter = ImperialMeasurement (1.0936*x) Yard
| u==Liter = ImperialMeasurement (0.2642*x) Gallon
| u==KiloGram = ImperialMeasurement (2.2046*x) Pound
```

```
convert (ImperialMeasurement x u)
| u==Yard = MetricMeasurement (0.9144*x) Meter
| u==Gallon = MetricMeasurement (3.7854*x) Liter
| u==Pound = MetricMeasurement (0.4536*x) KiloGram
```

Full code listing here
4. Compile and Test

```
Prelude> let m = MetricMeasurement 2 Meter
Prelude> convert m
ImperialMeasurement 2.18722 Yard
Prelude> convert (convert m)
MetricMeasurement 2 Meter
```

Remember, Haskell is lazy, and so are we.

Creating getter functions is monotonous!

```
data Point = Point Double Double
xval :: Point -> Double
xval (Point x _) = x
yval :: Point -> Double
yval (Point _ y) = y
```

Haskell provides a shortcut called record syntax

```
data Point = Point { xval::Double, yval::Double }
```

It makes code more readable

```
let a = Point 2 3
let b = Point {xval = 2, yval = 3}
```

There's more to write, but anyone can easily figure out what's going on.

Especially for large data types!

Oh yea, even types can have arguments.

```
data ThreeThings a = ThreeThings a a a deriving (Show)
```

`ThreeThings`

is bundle of three identical types
```
Prelude> ThreeThings 1 2 3
ThreeThings 1 2 3
Prelude> ThreeThings "hello" "there" "lad"
ThreeThings "hello" "there" "lad"
```

We can export useful code to a module

so that other projects can reuse them.

Let's make a module out of our

`convert`

code.
Step 1

```
module MyData
(MetricUnit(..),
ImperialUnit(..),
Measurement(..),
convert)
where
```

No step 2. That was easy!

Note: your file name should be the same as your module name!Use `convert`

from anywhere!

```
-- OtherFile.hs
import MyData
```

```
-- display a measurement in Metric
reportMeasurement :: Measurement -> String
```

```
-- if metric, print out the measurement
reportMeasurement (MyData.MetricMeasurement x u)
= (show x) ++ " " ++ (show u)
```

```
-- otherwise if imperial, convert the measurement and try again
reportMeasurement m
= reportMeasurement (convert m)
```

- Fill out this week's form!
- Create a binary tree data type of Int nodes
- Write a function to sum all nodes in the tree

`data Tree = ... `

`add :: Tree -> Int`