This document provides an overview of typeclasses in Haskell. It explains that typeclasses define properties that types can have, like being comparable (Eq) or ordered (Ord). Functions can constrain their parameters to only allow types that are members of certain typeclasses. The document also demonstrates how to define your own data types and derive standard typeclass instances or implement them yourself. This allows user-defined types to work with functions that require those typeclasses.
1. Type-Builder
Sebastian Rettig
In Haskell normally you understand aa function by reading
In Haskell normally you understand function by reading
Function Name ++ Parameter Types++Result Type.
Function Name Parameter Types Result Type.
2. Functional Programming
● No Variables
● Functions only, eventually stored in
Modules
– Behavior do not change, once defined
– → Function called with same parameter
calculates always the same result
● Function definitions (Match Cases)
● Recursion (Memory)
3. Haskell Features
● Pure Functional Programming Language
● Lazy Evaluation
● Pattern Matching and Guards
● List Comprehension
● Type Polymorphism
4. Static Type System
● type of every expression is known at
compile time
– use operation with not compatible types
– → program won't compile
– → saver code
5. Nice to remember (1)
● Types:
– starts with uppercase letter
– e.g.:
● Bool
● Int
● String
● [Int]
● (Bool, Char)
● Integer
6. Nice to remember (2)
● Typevariables
– to define generic types
– e.g.:
● maxList :: [a] -> a
● fst :: (a,b) -> a
● snd :: (a,b) -> b
– Typevariables a and b can contain every
type (including the same type)
7. Nice to remember (3)
● GHCi Commands (Interpreter):
– :t ← returns the function header (type)
– :t tail
tail :: [a] -> [a]
– :t 2 == 4
2 == 4 :: Bool
– :t "HELLO!"
"HELLO!" :: [Char]
– :i ← returns the function definition (interface)
– :i tail
tail :: [a] -> [a] --
Defined in GHC.List
8. Type Polymorphism (1)
● Statically typed, but Compiler can read type from
context (type inference)
● → no need to set type explicitly (but preferred!)
● → makes function more generic for different
kinds of types (type polymorphism)
– Why should I use quicksort :: [Int] -> [Int]
– even if I also want to sort character?
Hugs> quicksort ['f','a','d','b']
"abdf"
9. Type Polymorphism (2)
● the header could be
fact :: Int -> Int
maxList :: [Int] -> Int
● but is only limited to Int, but maxList could also
handle Char
● → why not make it generic?
maxList :: [a] -> a
● but what happens, if the corresponding Type is not
comparable or cannot be ordered?
10. Type Polymorphism (3)
● Solution: use Typeclasses
maxList :: (Ord a) => [a] -> a
● then we can be sure to use (<,<=, ==, /=, >=, >)
● function header can contain multiple typeclasses
maxList :: (Ord a, Eq b) => [a] -> [b] -> a
● In Haskell-Interpreter: to list the function header
:t <function_name>
11. Typeclasses (1)
● define properties of the types
● like an interface
● Typeclasses:
– Eq can be compared
– Ord can be ordered (>, <, >=, <=) (extending Eq)
– Show can be shown as string
– Read opposite of Show
– Enum sequentially ordered types (can be enumerated
and usable in List-Ranges ['a'..'e'])
12. Typeclasses (2)
● Typeclasses:
– Bounded upper/lower bound (minBound, maxBound)
– Num types behave like numbers (must already be Show, Eq)
– Integral contains only integrals (subclass of Num)
– Floating corresponding real numbers (subclass of Num)
● if all Types of tuple are in same Typeclass → Tuple also in
Typeclass
13. Let's build own types
● first look how existing types are defined:
– data Bool = False | True
●
data keyword is used to define a type
●
Bool is the type
● okay, let's define one, e.g.:
– data TrafficLight = Red | Yellow | Green
14. Value Constructors (1)
● or a more complex one, e.g.:
– data Shape =
Circle Float Float Float |
Rectangle Float Float Float Float
●
Circle and Rectangle are Value Constructors
● can contain types which holds the specific
values
●
Question: What is Red, Yellow, Green in
– data TrafficLight = Red | Yellow | Green
?
15. Value Constructors (2)
● Value Constructors are just functions which:
– get parameters of a specific type
– and returns the defined type
●
→ you can do: :t Circle
Circle :: Float -> Float -> Float -> Shape
● to look at the Type definition , you can use:
:i Shape
●
Question: What is the result of: :t Red in
data TrafficLight = Red | Yellow | Green
?
16. Let's use our type
● e.g.: calculate the surface of a Shape
surface :: Shape -> Float
surface (Circle _ _ r) = pi * r ^ 2
surface (Rectangle x1 y1 x2 y2) =
(abs $ x2 - x1) * (abs $ y2 - y1)
●
→ and call it:
Main> surface (Circle 1 2 3)
28.274334
● sometimes the parenthesis are annoying
→ better readability with $
surface $ Circle 1 2 3
17. Type Handling
● Why not just use (Float, Float, Float)?
– can define everything (e.g.: vector) → no context
– needs two functions, one for Circle and one for
Rectangle
● in Haskell normally you understand a function by
reading
– function name + parameter types + result type
● → you should write explicitly the function header
● → except for really small functions
18. Another Function
● e.g.: move left a Shape
moveLeft :: Shape -> Float -> Shape
moveLeft (Circle x y r) m = (Circle (x-m) y r)
moveLeft (Rectangle x1 y1 x2 y2) m =
(Rectangle (x1-m) y1 (x2-m) y2)
●
→ and call it:
Main> moveLeft (Circle 1 2 3) 2
No instance for (Show Shape)
arising from a use of `print'
Possible fix: add an instance declaration
for (Show Shape)
In a stmt of an interactive GHCi command:
print it
19. Membership of a Typeclass
● What happens?
– GHCi want's to print out (String) the result
– the Typeclass Show converts a type to String
● → Type must be part of the Typeclass Show
● two ways to solve this:
– inherit from existing implementation of types
you use
– implement the specific typeclass functions by
yourself
20. Inherit Membership (1)
● If used types are already member of the Typeclass
– Haskell use the implementation of this types to
print the specific values
– Value Constructor can generally be printed out
– you only need to derive from the
Implementation
21. Inherit Membership (2)
● first check if Float has already a membership:
data Float = GHC.Types.F# GHC.Prim.Float# --
Defined in GHC.Types
…
instance Show Float -- Defined in GHC.Float
…
● okay, then we can use the deriving keyword
data Shape =
Circle Float Float Float |
Rectangle Float Float Float Float
deriving (Show)
● and call again: moveLeft (Circle 1 2 3) 2
Circle (-1.0) 2.0 3.0
22. Inherit Membership (3)
● Float has also membership of Typeclass Ord:
instance Ord Float -- Defined in GHC.Classes
instance Eq Float -- Defined in GHC.Classes
● we can also derive from Ord but also from Eq
data Shape =
Circle Float Float Float |
Rectangle Float Float Float Float
deriving (Show, Eq, Ord)
● and we can use: maxList :: (Ord a) => [a] -> a
maxList [(Circle 1 2 3), (Circle 2 3 4)]
– returns: Circle 2.0 3.0 4.0
23. Inherit Membership (4)
● to check our new Typeclass memberships:
data Shape = Circle Float Float Float |
Rectangle Float Float Float Float
-- Defined at type.hs:3:6-10
instance Eq Shape -- Defined at type.hs:3:91-92
instance Ord Shape -- Defined at type.hs:3:95-97
instance Show Shape -- Defined at type.hs:3:85-88