Typeclasses and Datatypes
A typeclass is a set functions that a datatype must implement when the datatype in context is an instance of said typeclass.
To make use of Typeclasses, the concept of Datatypes must first be introduced.
What is a Datatype?
A datatype is a kind of variable which can be used in a programming language. In most cases, built-in datatypes include:
In Haskell, a developer may implement their own datatype by using the
-- Person has a name and age data Person = Person [Char] Integer deriving Show
In this example the
deriving Show fragment of the datatype definition means that the
Person adheres to the
Show typeclass. We will discuss later in the post.
Person is both a type and a constructor. A constructor is a function which creates instances of a type. Another way of defining the
Person datatype could have been:
-- Person has a name and age data Person = MakePerson [Char] Integer deriving Show
There are no rules which enforce the name of the constructor, it is up to the developer.
In this case, Haskell knows of the type
Person cannot be used as a variable. You would have to use
MakePerson instead. There are no rules enforcing the name of the constructor.
With this knowledge of datatypes, typeclasses can now be discussed.
Using a Built-in Typeclass
Datatypes in Haskell that compare with one another, are instances of the equality typeclass. Internally, it is defined in the following way.
class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool
class keyword defines a typeclass name and the set of functions a datatype must implement. Any externally defined datatype must conform to
Eq in order to be compared with
-- Person has name and age data Person = Person [Char] Integer deriving Show instance Eq Person where (==) person1 person2 = let (Person name1 age1) = person1 (Person name2 age2) = person2 in (name1 == name2) && (age1 == age2) (/=) person1 person2 = not (person1 == person2)
Person can implement its own versions of
Person variables can now be compared, just like the built-in types.
-- test subjects -- p1 = Person "Eric" 23 p2 = Person "Eric" 23 p3 = Person "John" 23 p4 = Person "John" 36 -- test results -- result1 = p1 == p2 -- True result2 = p1 == p3 -- False result3 = p3 /= p4 -- True
Person can become an instance of
deriving Show can be omitted.
data Person = Person [Char] Integer instance Show Person where show p1 = let (Person name age) = p1 in "Hi, " ++ name show (Person "Eric" 23) -- Hi, Eric
Defining a Typeclass
External typeclasses can be defined in Haskell and be used to enforce the implementation of functions on datatypes.
-- Person has name, age, hair color as string, eyes as a string data Person = Person [Char] Integer String String deriving Show class PhysicalFeatures a where hairColor :: a -> String -> Bool eyeColor :: a -> String -> Bool
As before, the datatype
Person can now adhere to the
PhysicalFeatures typeclass by using the
instance PhysicalFeatures Person where hairColor p1 hairColor = let (Person _ _ personHair _) = p1 in personHair == hairColor eyeColor p1 eyeColor = let (Person _ _ _ personEye) = p1 in personEye == eyeColor
Finally, instances of
Person can use functions defined in the
-- test subjects -- p1 = Person "Eric" 23 "Brown" "Brown" p2 = Person "Eric" 23 "Brown" "Green" p3 = Person "John" 23 "Blonde" "Blue" p4 = Person "John" 36 "Red" "Hazel" -- check physical features -- result1 = hairColor p1 "Brown" -- True result2 = hairColor p2 "Blonde" -- False result3 = eyeColor p3 "Brown" -- False result4 = eyeColor p4 "Hazel" -- True
In Haskell, typeclasses can be used to define the functionality of a type. A
typeclass is defined using the
class keyword. Types are defined using the
data keyword, and they become instances of a typeclass by using the