Sunday, 1 May 2016

Haskell: How to define a type class


A type class is a set of functions, along with their signature. By using the keyword ‘class’, you can define a type class.

Syntax
class TypeClassName a where
    function1 :: type1
    function2 :: type2
       ....
       ....
    functionN :: typeN

CustomTypeClasses.hs
class Equal a where

    isEquals :: a -> a -> Bool


Above snippet says that I defined a type class Equal, and this type class declare one function isEquals, which takes two arguments of type ‘a’ and return True if they are equal, else False. 'a' is a type variable, it refers instance types of this Type class Equal.

Go to GHCi prompt and get the type of the function ‘isEquals’.

*Main> :t isEquals
isEquals :: Equal a => a -> a -> Bool

Observe the signature, it is telling variable ‘a’ is of type Equal.

What is instance type?
An instance type of a typeclass is any type that implements the functions defined in the typeclass.
Prelude> :load CustomTypeClasses.hs 
[1 of 1] Compiling Main             ( CustomTypeClasses.hs, interpreted )
Ok, modules loaded: Main.
*Main> 
*Main> 
*Main> :t isEquals
isEquals :: Equal a => a -> a -> Bool


isEquals :: Equal a => a -> a -> Bool
You can interpret above statement like, type 'a' must be an instance of Equal, and isEqual method takes two arguments of type 'a' and return True if they are equal, else False.

Define isEquals method for custom type
I am going to define isEquals method for my custom types Engineer, Manager, Director.

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


CustomTypeClasses.hs
import CustomTypes

class Equal a where

    isEquals :: a -> a -> Bool

instance Equal Employee where
    isEquals (Engineer firstName1 lastName1 empId1) (Engineer firstName2 lastName2 empId2) =
        (firstName1 == firstName2) && (lastName1 == lastName2) && (empId1 == empId2)

    isEquals (Manager firstName1 lastName1 empId1 noOfReportees1) (Manager firstName2 lastName2 empId2 noOfReportees2) =
        (firstName1 == firstName2) && (lastName1 == lastName2) && (empId1 == empId2)

    isEquals (Director firstName1 lastName1 empId1 noOfReportees1) (Director firstName2 lastName2 empId2 noOfReportees2) =
        (firstName1 == firstName2) && (lastName1 == lastName2) && (empId1 == empId2)

*Main> :load CustomTypeClasses.hs 
[1 of 2] Compiling CustomTypes      ( CustomTypes.hs, interpreted )
[2 of 2] Compiling Main             ( CustomTypeClasses.hs, interpreted )
Ok, modules loaded: Main, CustomTypes.
*Main> 
*Main> let engineer1 = Engineer "Hari Krishna" "Gurram" 1
*Main> let engineer2 = Engineer "Hari" "Gurram" 1
*Main> let engineer3 = Engineer "Hari Krishna" "Gurram" 1
*Main> 
*Main> isEquals engineer1 engineer2
False
*Main> isEquals engineer1 engineer3
True
*Main> 


Suppose, you want to add isNotEquals function, which return True, when elements are not equal, False when elements are equal.

CustomTypeClasses.hs
import CustomTypes

class Equal a where

    isEquals :: a -> a -> Bool
    isNotEquals :: a -> a -> Bool

instance Equal Employee where
    isEquals (Engineer firstName1 lastName1 empId1) (Engineer firstName2 lastName2 empId2) =
        (firstName1 == firstName2) && (lastName1 == lastName2) && (empId1 == empId2)

    isEquals (Manager firstName1 lastName1 empId1 noOfReportees1) (Manager firstName2 lastName2 empId2 noOfReportees2) =
        (firstName1 == firstName2) && (lastName1 == lastName2) && (empId1 == empId2)

    isEquals (Director firstName1 lastName1 empId1 noOfReportees1) (Director firstName2 lastName2 empId2 noOfReportees2) =
        (firstName1 == firstName2) && (lastName1 == lastName2) && (empId1 == empId2)


Try to load CustomTypeClasses.hs file, compiler gives you warning like isNotEquals is not implemented for type Employee.
*Main> :load CustomTypeClasses.hs 
[1 of 2] Compiling CustomTypes      ( CustomTypes.hs, interpreted )
[2 of 2] Compiling Main             ( CustomTypeClasses.hs, interpreted )

CustomTypeClasses.hs:8:10: Warning:
    No explicit implementation for
      isNotEquals
    In the instance declaration for Equal Employee
Ok, modules loaded: Main, CustomTypes.


One way to get rid of this warning is implement 'isNotEquals' function for all Engineer, Manager, Director. Another simple way to implement is, any way we know isNotEquals function is completely opposite to isEquals function, we can add following line in CustomTypeClasses.hs file.

isNotEquals :: a -> a -> Bool
isNotEquals x y = not (isEquals x y)

isNotEquals is not( isEquals) function, (Given default implementation), that means types that implement isEquals function, no need to implement isNotEuals function. This is called providing default implementation for a type class function.

CustomTypeClasses.hs
import CustomTypes

class Equal a where

    isEquals :: a -> a -> Bool

    isNotEquals :: a -> a -> Bool
    isNotEquals x y = not (isEquals x y)

instance Equal Employee where
    isEquals (Engineer firstName1 lastName1 empId1) (Engineer firstName2 lastName2 empId2) =
        (firstName1 == firstName2) && (lastName1 == lastName2) && (empId1 == empId2)

    isEquals (Manager firstName1 lastName1 empId1 noOfReportees1) (Manager firstName2 lastName2 empId2 noOfReportees2) =
        (firstName1 == firstName2) && (lastName1 == lastName2) && (empId1 == empId2)

    isEquals (Director firstName1 lastName1 empId1 noOfReportees1) (Director firstName2 lastName2 empId2 noOfReportees2) =
        (firstName1 == firstName2) && (lastName1 == lastName2) && (empId1 == empId2)

*Main> :load CustomTypeClasses.hs 
[1 of 2] Compiling CustomTypes      ( CustomTypes.hs, interpreted )
[2 of 2] Compiling Main             ( CustomTypeClasses.hs, interpreted )
Ok, modules loaded: Main, CustomTypes.
*Main> 
*Main> let engineer1 = Engineer "Hari Krishna" "Gurram" 1
*Main> let engineer2 = Engineer "Hari" "Gurram" 1
*Main> 
*Main> isEquals engineer1 engineer2
False
*Main> 
*Main> isNotEquals engineer1 engineer2
True
*Main>



Previous                                                 Next                                                 Home

No comments:

Post a Comment