H. Conrad Cunningham
5 November 2018
Copyright (C) 2017, 2018, H. Conrad Cunningham
Acknowledgements: I originally created these slides in Fall 2017 to accompany what is now Chapter 23, Overloading and Type Classes, in the 2018 version of the textbook Exploring Languages with Interpreters and Functional Programming.
Browser 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 2018 is a recent version of Firefox from Mozilla.
Source Code: The Haskell module for this chapter is
in 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”
Overloading — 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!
eqBool
elemGen :: (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)
Define class Eq
as set of
types with (==)
defined
Make type Bool
instance of class (also others like Int
, Char
)
Make list in Eq
if element
is also
Left side of equation 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
Visible
No constraints on the conversion to strings
size
must return Int
, finite
and bounded
Ord
is subclass of Eq
class 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
Ord
For <=
and all
x
, y
,z
in type’s set (total
order)
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
instance
==
overloaded from different instancesclass
OrdVis
has
multiple inheritance — reuse methods of both
Must satisfy type class laws for Ord
and Visible
Prelude classes in Section 6.3 of the Haskell 2010 Language
Typeclassopedia by Brent Yorgey
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 (in Java from 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 Java/C++ overloading where functions have different types, same name
Haskell objects cannot be implicitly coerced, types have no
default parent — Java has universal base class Object
(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’s implicit classes/parameters enable similar type enrichment idiom
PureScript supports Haskell-like type classes
Idris supports interfaces, generalization of Haskell type classes
JavaScript has functional 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
Comparison to classes in other languages
The Haskell code for this chapter is file TypeClassMod.hs
.