Saturday, 30 April 2016

Haskell: Polymorphism


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.






Previous                                                 Next                                                 Home

No comments:

Post a Comment