Consider I was designing a Monopoly game:
data Board = GO | A1 | CC1 | A2 | T1 | R1 | B1 | CH1 | B2 | B3 |
JAIL | C1 | U1 | C2 | C3 | R2 | D1 | CC2 | D2 |
The simplest option is to make Board an instance of Bounded (can be auto derived as well), and use the following helper functions:
next :: (Enum a, Bounded a) => a -> a
next = turn 1
prev :: (Enum a, Bounded a) => a -> a
prev = turn (-1)
turn :: (Enum a, Bounded a) => Int -> a -> a
turn n e = toEnum (add (fromEnum (maxBound `asTypeOf` e) + 1) (fromEnum e) n)
where
add mod x y = (x + y + mod) `rem` mod
Example Use:
> next H2
G0
> prev G0
H2
> next F1
F2
(inspired by the the thread at http://www.mail-archive.com/haskell-cafe@haskell.org/msg37258.html ).
If you really need to use succ and pred instead, I don't believe there is any laws regarding implementations of Enum such that succ (succ x) /= x for all x (even though that is how most work). Therefore you could just write a custom implementation of Enum for your type that exhibits the wraparound you desire:
instance Enum Board where
toEnum 0 = G0
toEnum 1 = A1
...
toEnum 40 = H2
toEnum x = toEnum (x `mod` 40)
fromEnum G0 = 0
fromEnum A1 = 1
...
fromEnum H2 = 40
That is very tedious to implement though. Also, the type shouldn't also implement Bounded when using a circular definition of Enum, as that breaks a rule regarding Bounded that succ maxBound should result in a runtime error.