Haskell supports two kinds of
polymorphism.
a.
Parametric polymorphism
b.
ad-hoc polymorphism
a.
Parametric polymorphism
When a function signature has type
variables in it, it can able to accept any type of arguments. As per
convention, type variables are defined by using lower case letters. If you come
from Java background, you can assume type variables as generics. If you come
from C++ background, you can assume type variables as templates.
For example, Following function takes
list of any type and return number of elements in it.
ListUtil.hs
lengthOfList :: [a] -> Integer lengthOfList [] = 0 lengthOfList (x:xs) = 1 + lengthOfList xs
lengthOfList function takes a list of
any type and return the length of the list.
Prelude> :load ListUtil.hs [1 of 1] Compiling Main ( ListUtil.hs, interpreted ) Ok, modules loaded: Main. *Main> *Main> lengthOfList "abcdef" 6 *Main> lengthOfList [1, 2, 3, 4] 4 *Main> lengthOfList [1.1] 1 *Main>
A function can have more than one type
variable.
TupleUtil.hs
getFirst :: (a, b, c) -> a getFirst (x, _, _) = x getSecond :: (a, b, c) -> b getSecond (_, x, _) = x getThird :: (a, b, c) -> c getThird (_, _, x) = x
Prelude> :load TupleUtil.hs [1 of 1] Compiling Main ( TupleUtil.hs, interpreted ) Ok, modules loaded: Main. *Main> *Main> getFirst (1, "PTR", 5.3) 1 *Main> *Main> getSecond (1, "PTR", 5.3) "PTR" *Main> *Main> getThird (1, "PTR", 5.3) 5.3 *Main>
Polymorphism
Vs data types
Polymorphsim is available in data types
also.
EmployeeUtil.hs
{- Define an employee type -} data Employee a = Engineer {name :: String, engineerId :: a} | Manager {name :: String, engineerIds :: [a], managerId :: a} | Director {name :: String, managerIds :: [a], directorId :: a} deriving Show
data
Employee a = Engineer {name :: String, engineerId :: a}
Observe above statement, engineerId type
is ‘a’, which we can define at run time, it can be String, int etc.,
*Main> :load EmployeeUtil.hs [1 of 1] Compiling Main ( EmployeeUtil.hs, interpreted ) Ok, modules loaded: Main. *Main> *Main> let engineer1 = Engineer "Krishna" 123 *Main> let engineer2 = Engineer "Krishna" "E529432" *Main> *Main> :t engineer1 engineer1 :: Num a => Employee a *Main> *Main> :t engineer2 engineer2 :: Employee [Char]
Just like we defined multiple type
variables to a function arguments, we can also define a type using multiple
type variables.
EmployeeUtil.hs
{- Define an employee type -} data Employee a b c= Engineer {name :: String, engineerId :: a} | Manager {name :: String, engineerIds :: [a], managerId :: b} | Director {name :: String, managerIds :: [b], directorId :: c} deriving Show
*Main> :load EmployeeUtil.hs [1 of 1] Compiling Main ( EmployeeUtil.hs, interpreted ) Ok, modules loaded: Main. *Main> *Main> let manager = Manager "Anand" [2, 3, 5] "E432123" *Main> *Main> :t manager manager :: Num a => Employee a [Char] c
Ad-hoc
polymorphism
Ad-hoc polymorphism is a kind of
polymorphism in which polymorphic functions can be applied to arguments of
different types. Haskell uses type classes for Ad-hoc polymorphism. I recommend
you to go through type classes and the n come to this section, otherwise it is
very difficult to understand.
Following
are some of examlpes of ad-hoc polymorphism
a. The ==, <, >, <=, >=, /=
operators work on numbers and many other types.
b. Numeric operators like +, -, *, / able
to work on many different kinds of numbers.
Let me try to explain with simple
example. Consider the equals (==) function, it return True, if two variables
are equal, else False. Following is the singnature of == function.
Prelude> :t (==)
(==) :: Eq a => a -> a -> Bool
Observe type signature, ‘Eq a’ is
separated from rest of signature by =>. ‘Eq a’ constrain that, == function
is applied to a variable of type a, which is an instance of type class Eq.
CustomType.hs
{- Synonyms for existing types-} type Id = Int type Name = String type Year = Int type Month = Int type Day = Int type Salary = Double type BirthDate = Date {- Custom Types -} data Date = Date Year Month Day data Employee = Engineer Id Name BirthDate | Manager Id Name Salary BirthDate
Now create two
variables of type Engineer and try to compare.
*Main> :load CustomType.hs [1 of 1] Compiling Main ( CustomType.hs, interpreted ) Ok, modules loaded: Main. *Main> *Main> let date1 = Date 1989 5 6 *Main> let engineer1 = Engineer 123 "Krishna" date1 *Main> let engineer2 = Engineer 123 "Krishna" date1 *Main> *Main> engineer1 == engineer2 <interactive>:31:11: No instance for (Eq Employee) arising from a use of ‘==’ In the expression: engineer1 == engineer2 In an equation for ‘it’: it = engineer1 == engineer2
Oops I got an error, == operator is not
working on my custom type. Observe error message, it is telling ‘==’ function
requires an instance of type class Eq, to perform equality checks. To solve
above issue, either we need to derive Eq type class (or) provide definition of
== function, by using instance keyword. If you derive Eq class, it provides
default implementation for your custom type. I am going to use instance keyword
to provide my own definition for (==) function. Update CustomType.hs like
below.
CustomType.hs
{- Synonyms for existing types-} type Id = Int type Name = String type Year = Int type Month = Int type Day = Int type Salary = Double type BirthDate = Date {- Custom Types -} data Date = Date Year Month Day data Employee = Engineer Id Name BirthDate | Manager Id Name Salary BirthDate getId (Engineer empId _ _) = empId getId (Manager empId _ _ _) = empId instance Eq Employee where (==) emp1 emp2 = (getId emp1 == getId emp2)
Now you can
call == function on any variable, which is of type Employee.
*Main> :load CustomType.hs [1 of 1] Compiling Main ( CustomType.hs, interpreted ) Ok, modules loaded: Main. *Main> *Main> let date1 = Date 1989 5 6 *Main> let date2 = Date 1988 6 6 *Main> *Main> let engineer1 = Engineer 123 "Krishna" date1 *Main> let engineer2 = Engineer 123 "ptr" date2 *Main> let engineer3 = Engineer 321 "Krishna" date1 *Main> *Main> engineer1 == engineer2 True *Main> engineer1 == engineer3 False *Main> let manager1 = Manager 123 "Anand" 123456 date1 *Main> let manager2 = Manager 123 "Bandaru" 321456 date2 *Main> let manager3 = Manager 321 "Suhas" 345678 date1 *Main> *Main> manager1 == manager2 True *Main> manager1 == manager3 False *Main> manager2 == manager3 False
Difference
between parametric and Ad-hoc polymorphism
a. Parametric definitions are uniform: all
of their instances behave the same. For example, length of list, reverse a list
are examples of parametric polymorphism.
length :: [a] -> Int
length [] = 0
length (x:xs) = 1 + length xs
Where as Ad-hoc polymorphism, allows a
polymorphic value to exhibit different behaviors when “viewed” at different
types.
No comments:
Post a Comment