March 2021
Understanding type class polymorphism by translation into higher-order functions (dictionary passing).
Code examples: https://github.com/pbv/tapf-classes
class Show a where
show :: a -> String
instance Show Bool where
show b = if b then "True" else "False"
instance Show Int where
show = showInt -- showInt :: Int -> String
print :: Show a => a -> IO ()
print x = putStrLn (show x)
How can this be implemented?
data ShowD a = ShowD { -- dictionary for Show
show :: a -> String
}
showDBool :: ShowD Bool -- instance for Bool
showDBool = ShowD {
show = \b -> if b then "True" else "False"
}
showDInt :: ShowD Int -- instance for Int
showDInt = ShowD { show = showInt }
print :: ShowD a -> a -> IO ()
print showD x = putStrLn (show showD x)
printIncr :: (ShowD a, NumD a) -> a -> IO ()
printIncr (showD, numD) x
= putStrLn (show showD (add numD x
(fromInt numD 1)))
Instances with typeclass constraints translate as functions that construct new dictionaries from existing ones.
instance Eq a where
eq :: a -> a -> Bool
instance (Eq a, Eq b) => Eq (a,b) where
eq (x,y) (x',y') = eq x x' && eq y y'
data EqD a = EqD { eq :: a -> a -> Bool }
eqDPair :: (EqD a, EqD b) -> EqD (a,b)
eqDPair (EqD eqA, EqD eqB)
= EqD (\(x,y) (x',y') ->
eqA x x' && eqB y y')
instance Eq a => Eq [a] where
eq [] [] = True
eq (x:xs) (y:ys) = eq x y && eq xs ys
-- `eq` used above at different types
eq _ _ = False
eqDList :: EqD a -> EqD [a]
eqDList (EqD eqA) = EqD eqL
where
eqL [] [] = True
eqL (x:xs) (y:ys) = eqA x y && eqL xs ys
eqL _ _ = False
Dictionaries behave similiarly to the method suite in an OO-language:
Unlike OO-methods, dictionaries are passed separately from data:
fromInt
)==
)