How to define (+) function for custom data type?

Viewed 130

I'm a new one And study from book:

data Day 
  = Monday 
  | Tuesday 
  | Wednesday 
  | Thursday 
  | Friday 
  | Saturday 
  | Sunday 
  deriving (Eq, Ord, Show, Read, Bounded, Enum)

I thought that I can write a func to use +, example: Monday + 1 = Tuesday

So:

:{                           
(+) :: Day -> Int -> Day     
(+) x y                      
  | y>0 = (+) (succ x) (y-1) 
  | y<0 = (+) (pred x) (y+1) 
  | y==0 = x                 
:}   

But ghci said it has a mistake:

? Couldn't match expected type ‘Int’ with actual type ‘Day’
? In the second argument of ‘(+)’, namely ‘(y + 1)’
  In the expression: (+) (pred x) (y + 1)
  In an equation for ‘+’:
      (+) x y
        | y > 0 = (+) (succ x) (y - 1)
        | y < 0 = (+) (pred x) (y + 1)
        | y == 0 = x

I don't know why but I tried another one:

:{                                
(+) :: (Enum a) =>  a -> Int -> a 
(+) x y                           
  | y>0 = (+) (succ x) (y-1)      
  | y<0 = (+) (pred x) (y+1)      
  | y==0 = x                      
:}                                

It works well, like:

ghci> Monday + 1     
Tuesday              
ghci> Monday + 3     
Thursday             
ghci> Thursday + (-2)
Tuesday              

But I still don't know what's wrong with Day -> Int -> Day.

1 Answers

When you define a function named (+), you are shadowing Prelude's addition function. In the body of (+) :: Day -> Int -> Day, you evaluate (y + 1), expecting an Int. But this new (+) function returns a Day! The more polymorphic version for Enum works, because Int has an Enum instance, and so (+) can be used on both types.

To get around this, you could simply give this operation a new name instead of shadowing (+). Or, you could be explicit in the recursive call when you want to use your new (+) and when the original one, by writing (y Prelude.+ 1).

Related