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 | D3 |
FP | E1 | CH2 | E2 | E3 | R3 | F1 | F2 | U2 | F3 |
G2J | G1 | G2 | CC3 | G3 | R4 | CH3 | H1 | T2 | H2
deriving (Show, Enum, Eq)
I want:
succ H2 == GO
But instead:
*** Exception: succ{Board}: tried to take `succ' of last tag in enumeration
Is there a typeclass for expressing an enumeration that wraps around?
A simpler solution than nanothief:
nextBoard :: Board -> Board
nextBoard H2 = GO
nextBoard t = succ t
I don't think you'll be able to use Enum directly for what you want, but this solution quickly wraps it to form the behaviour you want.
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/[email protected]/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.
I know this is an old question but I just had this problem and I solved it this way.
data SomeEnum = E0 | E1 | E2 | E3
deriving (Enum, Bounded, Eq)
-- | a `succ` that wraps
succB :: (Bounded a, Enum a, Eq a) => a -> a
succB en | en == maxBound = minBound
| otherwise = succ en
-- | a `pred` that wraps
predB :: (Bounded a, Enum a, Eq a) => a -> a
predB en | en == minBound = maxBound
| otherwise = pred en
The solution derives both Enum
and Bounded
but avoids abusing pred
and succ
as suggested.
Incidently, I found that having
allSomeEnum = [minBound..maxBound] :: [SomeEnum]
can be useful. That requires Bounded
.
There is a disgusting way to define an efficient wrapping Enum
instance without doing much by hand.
{-# LANGUAGE MagicHash #-}
import GHC.Exts (Int (..), tagToEnum#, dataToTag# )
-- dataToTag# :: a -> Int#
-- tagToEnum# :: Int# -> a
Now you can write
data Board = ... deriving (Eq, Ord, Bounded)
instance Enum Board where
fromEnum a = I# (dataToTag# a)
toEnum x | x < 0 || x > fromEnum (maxBound :: Board) =
error "Out of range"
toEnum (I# t) = tagToEnum# t
succ x | x == maxBound = minBound
| otherwise == toEnum (fromEnum x + 1)
pred x ....
With Eq
you can check if it's the last element.
next :: (Eq a, Enum a, Bounded a) => a -> a
next = bool minBound <$> succ <*> (/= maxBound)
You could define the typeclass and instance below. You would just need to also derive Bounded
class (Eq a, Enum a, Bounded a) => CyclicEnum a where
cpred :: a -> a
cpred d
| d == minBound = maxBound
| otherwise = pred d
csucc :: a -> a
csucc d
| d == maxBound = minBound
| otherwise = succ d
instance CyclicEnum Board
The example is from the amazing book: Haskell in Depth
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With