Joseph Haugh
University of New Mexico
As we saw before a type declaration/synonym is not powerful enough to express a recursive type such as
type Tree = (Int, [Tree])
However data declarations are!
Possibly the simplest example is the peano numbers:
data Nat = Zero | Succ Nat
A Nat is either Zero or the Succ of a Nat.
data Nat = Zero | Succ Nat
Let’s define a function which transforms a Nat into an Int:
nat2Int :: Nat -> Int
nat2Int Zero = 0
nat2Int (Succ n) = 1 + nat2Int n
Can we go the other way?
int2Nat :: Int -> Nat
int2Nat 0 = Zero
int2Nat n = Succ (int2Nat (n-1))
data Nat = Zero | Succ Nat
Could we use these functions to define addition over Nats?
add :: Nat -> Nat -> Nat
add m n = int2Nat (nat2Int m + nat2Int n)
Hmm works but it’s ugly! Could we define it without relying on these functions?
Define the add function from the previous slide but do it directly without the use of int2Nat and nat2Int.
data Nat = Zero | Succ Nat
add :: Nat -> Nat -> Nat
Define the add function from the previous slide but do it directly without the use of int2Nat and nat2Int.
data Nat = Zero | Succ Nat
add :: Nat -> Nat -> Nat
add Zero n = n
add (Succ m) n = Succ (add m n)
data Nat = Zero | Succ Nat
add :: Nat -> Nat -> Nat
add Zero n = n
add (Succ m) n = Succ (add m n)
add (Succ (Succ Zero)) (Succ Zero)
| { applying add }
Succ (add (Succ Zero) (Succ Zero))
| { applying add }
Succ (Succ (add Zero (Succ Zero)))
| { applying add }
Succ (Succ (Succ Zero))
Does this remind of you of another function we have defined?
data Nat = Zero | Succ Nat
add :: Nat -> Nat -> Nat
add Zero n = n
add (Succ m) n = Succ (add m n)
append :: [a] -> [a] -> [a]
append [] ys = ys
append (x:xs) ys = x : append xs ys
Recall in the review how I described a list:
A list of a is either empty or cons of an element and a list of a
How can we turn this into a concrete Haskell data type?
Try to define a data type which represents a list with the following description:
A list of a is either empty or cons of an a and a list of a
data List a = Nil | Cons a (List a)
data List a = Nil | Cons a (List a)
What type does Nil have?
nil :: List a
What type does [] have?
[] :: [a]
What type does Cons have?
Cons :: a -> List a -> List a
What type does (:) have?
(:) :: a -> [a] -> [a]
Let’s redefine our old friend length using our new List datatype:
data List a = Nil | Cons a (List a)
length' :: List a -> Int
length' Nil = 0
length' (Cons _ xs) = 1 + length' xs
data List a = Nil | Cons a (List a)
sum' :: List Int -> Int
sum' Nil = 0
sum' (Cons x xs) = x + sum' xs
Redefine the foldr function to work over the List data type:
data List a = Nil | Cons a (List a)
foldrList :: (a -> b -> b) -> b -> List a -> b
Redefine the foldr function to work over the List data type:
data List a = Nil | Cons a (List a)
foldrList :: (a -> b -> b) -> b -> List a -> b
foldrList _ z Nil = z
foldrList f z (Cons x xs) = f x (foldrList f z xs)
data List a = Nil | Cons a (List a)
foldlList :: (b -> a -> b) -> b -> List a -> b
foldlList _ z Nil = z
foldlList f z (Cons x xs) = foldlList f (f z x) xs
Does the right hand side differ from the normal foldl?
foldl :: (b -> a -> b) -> b -> [a] -> b
foldl f z [] = z
foldl f z (x:xs) = foldl f (f z x) xs
Define a data type which represents a Tree with the following description:
A Tree of a is either a Leaf of a or a Node of Tree of a, an a, and another Tree of a
data Tree a = Leaf a | Node (Tree a) a (Tree a)
Here is an example Tree
t :: Tree Int
t = Node (Node (Leaf 1) 3 (Leaf 4)) 5
(Node (Leaf 6) 7 (Leaf 9))
Let’s write a function, occurs, which like elem for lists search for a given element in a Tree
data Tree a = Leaf a | Node (Tree a) a (Tree a)
occurs :: Eq a => a -> Tree a -> Bool
occurs x (Leaf y) = x == y
occurs x (Node l y r) = x == y || occurs x l || occurs x r
Write a function, flatten, which transforms a Tree into a list in depth first order.
data Tree a = Leaf a | Node (Tree a) a (Tree a)
flatten :: Tree a -> [a]
Write a function, flatten, which transforms a Tree into a list in depth first order.
data Tree a = Leaf a | Node (Tree a) a (Tree a)
flatten :: Tree a -> [a]
flatten (Leaf x) = [x]
flatten (Node l x r) = flatten l ++ [x] ++ flatten r
What if our Tree was a search Tree? Meaning that if a value exists in the Tree and it is less than the current node’s value then it must be in the left subtree, if it is greater then it must be in the right subtree. Given that your argument has this form redefine occurs:
occursST :: Ord a => a -> Tree a -> Bool
What if our Tree was a search Tree? Meaning that if a value exists in the Tree and it is less than the current node’s value then it must be in the left subtree, if it is greater then it must be in the right subtree. Given that your argument has this form redefine occurs:
occursST :: Ord a => a -> Tree a -> Bool
occursST x (Leaf y) = x == y
occursST x (Node l y r)
| x == y = True
| x < y = occursST x l
| otherwise = occursST x r
We can define many different Tree types based on our needs, for example:
data Tree a = Leaf a | Node (Tree a) (Tree a)
data Tree a = Leaf | Node (Tree a) a (Tree a)
data Tree a b = Leaf a | Node (Tree a b) b (Tree a b)
data Tree a = Node a [Tree a]