How to do it...

  1. Open src/Main.hs for editing. We will use this file for using type classes. 
  2. Define a data type Month to describe a month in a year:
        data Month = January | February | March | April | May | June
| July | August | September | October | November |
December deriving Show

Note that we have still used automatic derivation from Show. We will illustrate Show later in the recipe.

  1. Next, implement the Enum class. The Enum class is responsible for generating a list of consecutive integers and expressions such as [1..10].  The Enum class provides this behavior by associating with Integer. Create an instance of the Enum class for the data type Month. Essentially, we need to implement two functions, toEnum and fromEnum, to convert from and to Integers:
        instance Enum Month where
toEnum 0 = January
toEnum 1 = February
toEnum 2 = March
toEnum 3 = April
toEnum 4 = May
toEnum 5 = June
toEnum 6 = July
toEnum 7 = August
toEnum 8 = September
toEnum 9 = October
toEnum 10 = November
toEnum 11 = December
toEnum n = toEnum $ n `rem` 12

fromEnum January = 0
fromEnum February = 1
fromEnum March = 2
fromEnum April = 3
fromEnum May = 4
fromEnum June = 5
fromEnum July = 6
fromEnum August = 7
fromEnum September = 8
fromEnum October = 9
fromEnum November = 10
fromEnum December = 11
  1. Implement the equality type class for our data type Month. It gives an ability to check equality among the values of our data type MonthDefine the function (==). We will use the previous definition of Enum to convert the values to Integer and then compare them: 
        instance Eq Month where
m1 == m2 = fromEnum m1 == fromEnum m2
  1. Now, implement the Ord type class. Ord stands for ordere, and it gives the ordering among the values of our the data type Month. We need to define a function compare and return the values of data type Ordering. We will again use the fact that we have already implemented the Enum type class and that Integers already implement the Ord type class. Hence, we will convert the values of Month to integers and then invoke its compare method:
        instance Ord Month where
m1 `compare` m2 = fromEnum m1 `compare` fromEnum m2

Note how we implemented the compare function using the in-fix notation. 

  1. So far we have implemented a data type (in this, and earlier recipes) which uses auto implementation for Show provided by the compiler, GHC. Now, we will implement a data type where we provide explicit implementation for Show and ReadImplement a data type, RoseTree, which is a n-ary tree. 
        data RoseTree a = RoseTree a [RoseTree a]
  1. Now, implement the Show type class. For Showwe have to implement a function show :: a -> String
        toString :: Show a => RoseTree a -> String -> String
toString (RoseTree a branches) =
( "<<" ++) . shows a . ('[':) . branchesToString branches .
(']':) . ( ">>" ++)
where
branchesToString [] r = r
branchesToString (x:[]) r = branchesToString [] (toString x ""
++ r)
branchesToString (x:xs) r = branchesToString xs (',' :
toString x "" ++ r)

Use the preceding function to implement Show:

        instance Show a => Show (RoseTree a) where
show tree = toString tree ""
  1. Now, implement a type class Read. The Read class does the reverse of the type class Showit reads the String value returned by Show and converts it back to a value of a type. Here, we will implement an instance of type class for RoseTree
        instance Read a => Read (RoseTree a) where
readsPrec prec ('<':'<':s) =
case readsPrec prec s of
[(a,t)] -> case readList t of
[(as,ts)] -> case ts of
('>':'>':ss) -> [(RoseTree a as, ss)]
_ -> []
_ -> []
_ -> []
readsPrec prec _ = []

readList xs =
let readList' ('[':ys) rs =
case readsPrec 0 ys of
[(r,zs)] -> readList' zs (r:rs)
_ -> readList' ys rs

readList' (',':ys) rs =
case readsPrec 0 ys of
[(r,zs)] -> readList' zs (r:rs)
_ -> []

readList' (']':ys) rs = [(rs,ys)]
readList' _ _ = []

in readList' xs []
  1. Now, use the implementation in the main function to use the preceding type classes:
        main :: IO ()
main = do
putStrLn "Enumerating months"
putStrLn $ show [January ..December]
putStrLn "Enumerating odd months"
putStrLn $ show [January,March .. December]
putStrLn $ "Equating months, January with itself : "
++ (show $ January == January)
++ " and January with February : "
++ (show $ January == February)
putStrLn $ "Using /= function"
putStrLn $ "Not equating months, January with itself : "
++ (show $ January /= January)
++ " and January with February : "
++ (show $ January /= February)
putStrLn $ "Comparing months, January with itself : "
++ (show $ January `compare` January)
++ " and January with February : "
++ (show $ January `compare` February)

putStrLn ""
putStrLn "Creating a tree"

let singleton = RoseTree 10 []
tree = RoseTree 10 [RoseTree 13 [RoseTree 11 []], RoseTree 7
[], RoseTree 5 [RoseTree 3 []]]

putStrLn ""
putStrLn $ "Showing singleton tree : " ++ show singleton
putStrLn $ "Showing tree : " ++ show tree

putStrLn ""
putStrLn $ "Read what you show -- show (read (show tree) )"
putStrLn $ "Singleton Tree - " ++ show (read (show singleton) ::
RoseTree Int)
putStrLn $ "Tree - " ++ show (read (show tree) :: RoseTree Int)
  1. Build and run the application:
      stack build
stack exec -- working-with-type-classes
  1. The output should look like this: