Polymorphic Functions
Operate on different kinds values
>>> double x = 2 * x
>>> yum x = x ++ " yum! yum!"
>>> doTwice double 10
40
>>> doTwice yum "cookie"
"cookie yum! yum!"
QUIZ
What is the value of quiz
?
A. True
B. False
C. Type Error
D. Run-time Exception
E. 101
With great power, comes great responsibility!
>>> doTwice (greaterThan 10) 0
36:9: Couldn't match type ‘Bool’ with ‘Int’
Expected type: Int -> Int
Actual type: Int -> Bool
In the first argument of ‘doTwice’, namely ‘greaterThan 10’
In the expression: doTwice (greaterThan 10) 0
The input and output types are different!
Cannot feed the output of (greaterThan 10 0)
into greaterThan 10
!
Polymorphic Types
But the type of doTwice
would have spared us this grief.
The signature has a type parameter t
re-use
doTwice
to incrementInt
or concatString
or …The first argument
f
must take inputt
and return outputt
(i.e.t -> t
)The second argument
x
must be of typet
Then
f x
will also have typet
… and we can callf (f x)
.
But f
unction is incompatible with doTwice
- if its input and output types differ
QUIZ
Lets make sure you’re following!
What is the type of quiz
?
A. a -> a
B. (a -> a) -> a
C. a -> b -> a -> b
D. a -> (a -> b) -> b
E. a -> b -> a
QUIZ
Lets make sure you’re following!
What is the value of quiz
?
apply x f = f x
greaterThan :: Int -> Int -> Bool
greaterThan x y = x > y
quiz = apply 100 (greaterThan 10)
A. Type Error
B. Run-time Exception
C. True
D. False
E. 110
Polymorphic Data Structures
Today, lets see polymorphic data types
which contain many kinds of values.
Recap: Data Types
Recall that Haskell allows you to create brand new data types
QUIZ
What is the type of MkRect
?
a. Shape
b. Double
c. Double -> Double -> Shape
d. (Double, Double) -> Shape
e. [(Double, Double)] -> Shape
Tagged Boxes
Values of this type are either two doubles tagged with Rectangle
or a list of pairs of Double
values tagged with Polygon
Data values inside special Tagged Boxes
Recursive Data Types
We can define datatypes recursively too
data IntList
= INil -- ^ empty list
| ICons Int IntList -- ^ list with "hd" Int and "tl" IntList
deriving (Show)
(Ignore the bit about deriving
for now.)
QUIZ
data IntList
= INil -- ^ empty list
| ICons Int IntList -- ^ list with "hd" Int and "tl" IntList
deriving (Show)
What is the type of ICons
?
A. Int -> IntList -> List
B. IntList
C. Int -> IntList -> IntList
D. Int -> List -> IntList
E. IntList -> IntList
Constructing IntList
Can only build IntList
via constructors.
EXERCISE
Write down a representation of type IntList
of the list of three numbers 1
, 2
and 3
.
Hint Recursion means boxes within boxes
Trees: Multiple Recursive Occurrences
We can represent Int
trees like
data IntTree
= ILeaf Int -- ^ single "leaf" w/ an Int
| INode IntTree IntTree -- ^ internal "node" w/ 2 sub-trees
deriving (Show)
A leaf is a box containing an Int
tagged ILeaf
e.g.
A node is a box containing two sub-trees tagged INode
e.g.
>>> itt = INode (ILeaf 1) (ILeaf 2)
>>> itt' = INode itt itt
>>> INode itt' itt'
INode (INode (ILeaf 1) (ILeaf 2)) (INode (ILeaf 1) (ILeaf 2))
Multiple Branching Factors
e.g. 2-3 trees
An example value of type Int23T
would be
which looks like
Parameterized Types
We can define CharList
or DoubleList
- versions of IntList
for Char
and Double
as
data CharList
= CNil
| CCons Char CharList
deriving (Show)
data DoubleList
= DNil
| DCons Char DoubleList
deriving (Show)
Don’t Repeat Yourself!
Don’t repeat definitions - Instead reuse the list structure across all types!
Find abstract data patterns by
- identifying the different parts and
- refactor those into parameters
A Refactored List
Here are the three types: What is common? What is different?
data IList = INil | ICons Int IList
data CList = CNil | CCons Char CList
data DList = DNil | DCons Double DList
Common: Nil
/Cons
structure
Different: type of each “head” element
Refactored using Type Parameter
Recover original types as instances of List
Polymorphic Data has Polymorphic Constructors
Look at the types of the constructors
That is, the Empty
tag is a value of any kind of list, and
Cons
takes an a
and a List a
and returns a List a
.
cList :: List Char -- list where 'a' = 'Char'
cList = Cons 'a' (Cons 'b' (Cons 'c' Nil))
iList :: List Int -- list where 'a' = 'Int'
iList = Cons 1 (Cons 2 (Cons 3 Nil))
dList :: List Double -- list where 'a' = 'Double'
dList = Cons 1.1 (Cons 2.2 (Cons 3.3 Nil))
Polymorphic Function over Polymorphic Data
Lets write the list length function
len
doesn’t care about the actual values in the list - only “counts” the number of Cons
constructors
Hence len :: List a -> Int
- we can call
len
on any kind of list.
>>> len [1.1, 2.2, 3.3, 4.4] -- a := Double
4
>>> len "mmm donuts!" -- a := Char
11
>>> len [[1], [1,2], [1,2,3]] -- a := ???
3
Built-in Lists?
This is exactly how Haskell’s “built-in” lists are defined:
Nil
is called[]
Cons
is called:
Many list manipulating functions e.g. in [Data.List][1] are polymorphic - Can be reused across all kinds of lists.
Generalizing Other Data Types
Polymorphic trees
Polymorphic 2-3 trees
data Tree23 a
= Leaf0
| Node2 (Tree23 a) (Tree23 a)
| Node3 (Tree23 a) (Tree23 a) (Tree23 a)
deriving (Show)
Kinds
List a
corresponds to lists of values of type a
.
If a
is the type parameter, then what is List
?
A type-constructor that - takes as input a type a
- returns as output the type List a
But wait, if List
is a type-constructor then what is its “type”?
- A kind is the “type” of a type.
Thus, List
is a function from any “type” to any other “type”, and so
QUIZ
What is the kind of ->
? That, is what does GHCi say if we type
A. *
B. * -> *
C. * -> * -> *
We will not dwell too much on this now.
As you might imagine, they allow for all sorts of abstractions over data.
If interested, see this for more information about kinds.