H. Conrad Cunningham
7 Novemver 2017
Copyright (C) 2017, H. Conrad Cunningham
Acknowledgements: These slides accompany the first 10 sections of Chapter 9 “Overloading and Type Classes” of the in-work textbook “Introduction to Functional Programming Using Haskell”. This chapter is an incomplete draft.
Advisory: The HTML version of this document may require use of a browser that supports the display of MathML. A good choice as of November 2017 is a recent version of Firefox from Mozilla.
Code: The Haskell code for this chapter is file TypeClassMod.hs.
Motivate overloading and type classes
Introduce type class laws
Extend concepts to inheritance and multiple constraints
Compare Haskell to other languages
Parametric polymorphism -- same name, same function definition for all types of arguments/results -- "polymorphism""
length: [a] -> IntOverloading -- same name, same functionality, but different function definitions depending upon type
(+) function takes two numbers of same type
elemBool :: Bool -> [Bool] -> Bool
elemBool x [] = False
elemBool x (y:ys) = eqBool x y || elemBool x ys
How can define eqBool?
eqBool :: Bool -> Bool -> Bool
eqBool True False = False
eqBool False True = False
eqBool _ _ = True
Not general!
eqBoolelemGen :: (a -> a -> Bool) -> a -> [a] -> Bool
elemGen eqFun x [] = False
elemGen eqFun x (y:ys) = eqFun x y || elemGen eqFun x ys
elemBool :: Bool -> [Bool] -> Bool
elemBool = elemGen eqBool
But elemGen too general! any a -> a -> Bool
Equality meaningless for some types (functions?)
Overload == operator for set of types
Restrict polymorphism in elem to types with (==) defined
Introduce type class for equality, then constrain polymorphism to that context (used before)
elem :: Eq a => a -> [a] -> BoolDefine class Eq as set of types with (==) defined
class Eq a where
(==) :: a -> a -> BoolMake type Bool instance of class (also others like Int, Char)
instance Eq Bool where
True == True = True
False == False = True
_ == _ = FalseMake list in Eq if element is also
instance Eq a => Eq [a] where
[] == [] = True
(x:xs) == (y:ys) = x == y && xs == ys
_ == _ = FalseLeft side of euation defined by right side
Right side must be previously defined operation (e.g. x == y) or terminating recursion (e.g. xs == ys)
class Eq a where -- from Prelude library
(==), (/=) :: a -> a -> Bool
-- Minimal complete definition: (==) or (/=)
x /= y = not (x == y)
x == y = not (x /= y)
Equality is equivalence relation in math. For all x, y, and z in type's set:
x == x is True.x == y if and only if y == x.x == y and y == z, then x == z.x /= y equivalent to not (x == y)
Type class laws -- must hold for all Eq instances
Reality intervenes! x == x for infinite? floating?
class Visible a where
toString :: a -> String
size :: a -> Int
instance Visible Char where
toString ch = [ch]
size _ = 1
instance Visible Bool where
toString True = "True"
toString False = "False"
size _ = 1
instance Visible a => Visible [a] where
toString = concat . map toString
size = foldr (+) 1 . map size
VisibleNo constraints on the conversion to strings
size must return Int, finite and bounded
Ord is subclass of Eqclass Eq a => Ord a where -- from Prelude
(<), (<=), (>), (>=) :: a -> a -> Bool
max, min :: a -> a -> a
-- Minimal complete definition: (<) or (>)
-- Must break circular definition
x <= y = x < y || x == y
x < y = y > x
x >= y = x > y || x == y
x > y = y < x
max x y | x >= y = x
| otherwise = y
min x y | x <= y = x
| otherwise = y
OrdOrd instance implements total order on type's set. For operation <= and all x, y, and z in the type's set
Reflexivity: x <= x is True
Antisymmetry: x <= y and y <= x, then x == y
Transitivity: if x <= y and y <= z, then x <= z
Trichotomy (comparability, totality): x <= y or y <= x
== (and /=) satisfy Eq type class laws
<, >, >=, max, and min satisfy definition in declaration
isort isort' :: Ord a => [a] -> [a]
isort' [] = []
isort' (x:xs) = insert' x (isort' xs)
insert' :: Ord a => a -> [a] -> [a]
insert' x [] = [x]
insert' x (y:ys)
| x <= y = x:y:ys -- overloaded <=
| otherwise = y : insert' x ys
vSort :: (Ord a,Visible a) => [a] -> String
vSort = toString . isort'
vLookupFirst :: (Eq a,Visible b) => [(a,b)] -> a -> String
vLookupFirst xs x = toString (lookupFirst xs x)
lookupFirst :: Eq a => [ (a,b) ] -> a -> [b]
lookupFirst ws x = [ z | (y,z) <- ws, y == x ]
instanceinstance (Eq a, Eq b) => Eq (a,b) where
(x,y) == (z,w) = x == z && y == w
== overloaded from different instancesclassclass (Ord a,Visible a) => OrdVis a
vSort :: OrdVis a => [a] -> String
OrdVis has multiple inheritance -- reuse methods of both
Must satisfy type class laws for Eq and Visible
Haskell class is collection of types; Java class similar to type
Haskell class similar to Java abstract class except Haskell has no data, Java only single inheritance
Haskell class similar to Java interface except Haskell has default method definitions (not in Java before Java 8)
Haskell instance is type, not object -- like concrete Java class that extends abstract classes or implements interface
Haskell separates type definition from method definition; Java mixes type and method definition
Haskell class methods correspond to Java instance methods
Each instance provides own definition for methods; class defaults correspond to default definitions in parent class
Haskell has no receiver object or mutable fields
Haskell class methods bound statically at compile time; Java bound dynamically at runtime
Java attaches identifying information to runtime object; Haskell only attaches logically
Haskell does not support C++ overloading where functions have different types and same name
Haskell objects cannot be implicitly coerced, types have no default parent
Java has cosmic base class Object for all reference types
Haskell class separates type from access control, uses modules for access control
Java mixes
First appeared in Haskell, but adopted in newer languages
Rust supports traits, limited form of type classes
Scala has implicit classes and parameters, which enable type enrichment idiom similar to type classes
PureScript supports Haskell-like type classes
Idris supports interfaces, generalization of Haskell type classes
JavaScript has libraries such as Ramda
Overloading function giving same name, "same functionality" but different definitions for different types
Type classes and instances
Type class laws to define "same functionality"
Multiple constraints, inheritance
The Haskell code for this chapter is file TypeClassMod.hs.