Lecture 6.0 — 2017-02-06
Type classes
This lecture is written in literate Haskell; you can download the raw source.
We revisited our Listlike type class…
class Listlike f where
nil :: f a
cons :: a -> f a -> f a
openCons :: f a -> Maybe (a,f a)
hd :: f a -> Maybe a
hd l =
case openCons l of
Nothing -> Nothing
Just (x,_) -> Just x
tl :: f a -> Maybe (f a)
tl l =
case openCons l of
Nothing -> Nothing
Just (_,xs) -> Just xs
isNil :: f a -> Bool
isNil l =
case openCons l of
Nothing -> True
Just _ -> False
foldRight :: (a -> b -> b) -> b -> f a -> b
foldRight f b l =
case openCons l of
Nothing -> b
Just (x,xs) -> f x (foldRight f b xs)
foldLeft :: (b -> a -> b) -> b -> f a -> b
foldLeft f b l =
case openCons l of
Nothing -> b
Just (x, xs) -> foldLeft f (f b x) xs
each :: (a -> b) -> f a -> f b
each f = foldRight (cons . f) nil
append :: f a -> f a -> f a
append xs ys = foldRight cons ys xs
…and its implementation using lists…
instance Listlike ([]) where
-- nil :: [a]
nil = []
-- cons :: a -> [a] -> [a]
-- cons x xs = x:xs
cons = (:)
-- openCons :: [a] -> Maybe (a,[a])
openCons [] = Nothing
openCons (x:xs) = Just (x,xs)
myConcat :: Listlike f => f (f a) -> f a
myConcat = foldRight append nil
But then we looked at an alternative implementation using union trees (a similar data structure is used in the union-find algorithm:
data UnionTree a =
Empty -- []
| Singleton a -- [a]
| Union { left :: UnionTree a, right :: UnionTree a }
deriving Show
instance Listlike UnionTree where
-- nil :: f a
nil = Empty
-- cons :: a -> f a -> f a
cons x xs = Union (Singleton x) xs
-- openCons :: f a -> Maybe (a,f a)
openCons Empty = Nothing
openCons (Singleton x) = Just (x,Empty)
openCons (Union l r) =
case openCons l of
Nothing -> openCons r
Just (x,l') -> Just (x,Union l' r)
ut1,ut2,ut3 :: UnionTree String
ut1 = Union (Singleton "hi") (Singleton "there")
ut2 = Union Empty Empty
ut3 = Union (Singleton "everybody") Empty
Observe that the same myConcat
function applies just as well to our union trees as they did to lists. You can think of type classes as being implicit parameters that get automatically filled in by Haskell.
ut = myConcat (Union (Singleton ut1) (Union (Singleton ut2) (Singleton ut3)))
We defined a few more:
toList :: Listlike f => f a -> [a]
toList l = foldRight (:) [] l
fromList :: Listlike f => [a] -> f a
fromList l = foldr cons nil l
Embarrassingly, I tried to show how Haskell sometimes can’t figure out which typeclass to use… but it turns out it will default to list when it’s available. We will, however, eventually find cases where Haskell can’t figure out which type class to use—in which case you’ll need to use a type annotation.