Sunday, 1 May 2016

Haskell: Type classes


By using type classes we can define a generic interface that contain common operations applicable to number of types. Let me explain with an example.

For example, I want to develop an application for company XYZ.  There are three types of employees (Engineer, Manager, Director). My custom types are look like below.

CustomTypes.hs
module CustomTypes where
    type FirstName = String
    type LastName = String
    type EmpId = Integer
    type NoOfReportees = Integer

    {- Define an employee type -}
    data Employee = Engineer FirstName LastName EmpId
                   | Manager FirstName LastName EmpId NoOfReportees
                   | Director FirstName LastName EmpId NoOfReportees


I just want to display employee information to the console. Following methods do the task.
showEngineer (Engineer firstName lastName empId) = (firstName ++ " : " ++ lastName ++ " : " ++ (show empId))

showManager (Manager firstName lastName empId noOfReportees) = (firstName ++ " : " ++ lastName ++ " : " ++ (show empId) ++ " : " ++ (show noOfReportees))

showDirector (Director firstName lastName empId noOfReportees) = (firstName ++ " : " ++ lastName ++ " : " ++ (show empId) ++ " : " ++ (show noOfReportees))


After adding above methods my CustomTypes.hs file looks like below.

CustomTypes.hs
module CustomTypes where
    type FirstName = String
    type LastName = String
    type EmpId = Integer
    type NoOfReportees = Integer

    {- Define an employee type -}
    data Employee = Engineer FirstName LastName EmpId
                   | Manager FirstName LastName EmpId NoOfReportees
                   | Director FirstName LastName EmpId NoOfReportees


    showEngineer (Engineer firstName lastName empId) = (firstName ++ " : " ++ lastName ++ " : " ++ (show empId))

    showManager (Manager firstName lastName empId noOfReportees) = (firstName ++ " : " ++ lastName ++ " : " ++ (show empId) ++ " : " ++ (show noOfReportees))

    showDirector (Director firstName lastName empId noOfReportees) = (firstName ++ " : " ++ lastName ++ " : " ++ (show empId) ++ " : " ++ (show noOfReportees))

Prelude> :load CustomTypes.hs 
[1 of 1] Compiling CustomTypes      ( CustomTypes.hs, interpreted )
Ok, modules loaded: CustomTypes.
*CustomTypes> 
*CustomTypes> let engineer1 = Engineer "Hari Krishna" "Gurram" 1
*CustomTypes> let manager1 = Manager "Anand" "Bandaru" 2 24
*CustomTypes> let director1 = Director "Sailaja" "PTR" 1234 58
*CustomTypes> 
*CustomTypes> showEngineer engineer1
"Hari Krishna : Gurram : 1"
*CustomTypes> 
*CustomTypes> showManager manager1
"Anand : Bandaru : 2 : 24"
*CustomTypes> 
*CustomTypes> showDirector director1
"Sailaja : PTR : 1234 : 58"
*CustomTypes> 


Lets go back to the code, Don't you think the functions showEngineer, showManager, showDirector are doing same task, I mean just displaying the values associated with their fields like FirstName, LastName, EmpId etc., What if there is a way, I can put this common functionality into some place, and my custom types can able to use this common functionality without duplicating the code. There type classes come into picture.

For Example, Haskell provide a built-in type class Show, which has function show in it.

show :: (Show a) => a -> String

show function takes an argument a, which is instance of class Show and convert a to String.

We can use the functionality provided by Show class by deriving (deriving (Show)) it in our custom types like below. Update CustomTypes.hs like below.

CustomTypes.hs
module CustomTypes where
    type FirstName = String
    type LastName = String
    type EmpId = Integer
    type NoOfReportees = Integer

    {- Define an employee type -}
    data Employee = Engineer FirstName LastName EmpId
                   | Manager FirstName LastName EmpId NoOfReportees
                   | Director FirstName LastName EmpId NoOfReportees
                   deriving (Show)

Prelude> :load CustomTypes.hs 
[1 of 1] Compiling CustomTypes      ( CustomTypes.hs, interpreted )
Ok, modules loaded: CustomTypes.
*CustomTypes> 
*CustomTypes> let engineer1 = Engineer "Hari Krishna" "Gurram" 1
*CustomTypes> let manager1 = Manager "Anand" "Bandaru" 2 24
*CustomTypes> let director1 = Director "Sailaja" "PTR" 1234 58
*CustomTypes> 
*CustomTypes> show engineer1
"Engineer \"Hari Krishna\" \"Gurram\" 1"
*CustomTypes> 
*CustomTypes> show manager1
"Manager \"Anand\" \"Bandaru\" 2 24"
*CustomTypes> 
*CustomTypes> show director1
"Director \"Sailaja\" \"PTR\" 1234 58"


You can get rid of escape sequences from output, by using putStrLn function.
Prelude> :load CustomTypes.hs 
[1 of 1] Compiling CustomTypes      ( CustomTypes.hs, interpreted )
Ok, modules loaded: CustomTypes.
*CustomTypes> 
*CustomTypes> let engineer1 = Engineer "Hari Krishna" "Gurram" 1
*CustomTypes> let manager1 = Manager "Anand" "Bandaru" 2 24
*CustomTypes> let director1 = Director "Sailaja" "PTR" 1234 58
*CustomTypes> 
*CustomTypes> show engineer1
"Engineer \"Hari Krishna\" \"Gurram\" 1"
*CustomTypes> 
*CustomTypes> show manager1
"Manager \"Anand\" \"Bandaru\" 2 24"
*CustomTypes> 
*CustomTypes> show director1
"Director \"Sailaja\" \"PTR\" 1234 58"


What are Type classes?
Type classes define number of functions and any types that are deriving these type classes can use these functions (or) can override this default behavior.

Is this type classes are same like classes in Java?
Absolutely not. They have more in common with interfaces, in that they specify a series of methods or values by their type signature, to be implemented by an instance declaration.


 
Previous                                                 Next                                                 Home

No comments:

Post a Comment