eazar001
eazar001

Reputation: 1601

Less redundant way to make instances of a "circular" enum typeclass

I have a snippet of code in which I have declared two datatypes. I've auto-derived both as members of the typeclass enum, however, I dislike that they are not "circular". By this, I mean that calling succ Sun should yield me Mon. succ Dec should result in Jan. So instead of writing my own enum I did this:

data WeekDay = Mon | Tue | Wed | Thu | Fri | Sat | Sun
             deriving (Enum, Show, Eq, Bounded)

data Month = Jan | Feb | Mar | Apr | May | Jun | July | Aug | Sep | Oct | Nov 
             | Dec
           deriving (Enum, Show, Eq, Bounded)

class Circ a where
  next :: Enum a => a -> a

instance Circ WeekDay where
  next a = if a == maxBound then minBound else succ a

instance Circ Month where      -- this is nearly identical to the one above
  next a = if a == maxBound then minBound else succ a

My question is: Is there a neater, less redundant way of writing this? In other words, I have two nearly identical instances written, with the datatype name (WeekDay vs. Month) being the only variable that changed.

Upvotes: 6

Views: 610

Answers (4)

Gabriella Gonzalez
Gabriella Gonzalez

Reputation: 35089

I would like to build on Ingo's answer by pointing out that you don't actually need to define a type class at all. You can just define the following function without invoking any additional type class:

next :: (Eq a, Bounded a, Enum a) => a -> a
next a = if a == maxBound then minBound else succ a

Now you don't have to declare instance Circ for all of your types.

Upvotes: 5

wit
wit

Reputation: 1622

You could add prev too

class (Enum a, Bounded a, Eq a) => Circ a where
     next :: a -> a
     next a = if a == maxBound then minBound else succ a
     prev :: a -> a
     prev a = if a == minBound then maxBound else pred a

Upvotes: 2

Ingo
Ingo

Reputation: 36339

class (Enum a, Bounded a, Eq a) => Circ a where
     next :: a -> a
     next a = if a == maxBound then minBound else succ a

instance Circ WeekDay
instance Circ Month

Upvotes: 11

Lee
Lee

Reputation: 144136

It appears you can do the following if you enable the FlexibleInstances and UndecidableInstances extensions:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}

instance (Eq a, Bounded a, Enum a) => Circ a where
  next e = if e == maxBound then minBound else succ e

Upvotes: 3

Related Questions