Haskell toEnum on data type throws range exception
Your problem is that Pitch
is implicitly bounded – i.e., it has a least and greatest element – but you don't reflect that boundedness in your code. The code
[Pitch O1 A ..]
desugars to
enumFrom (Pitch O1 A)
which keeps succ
ing the generated values until it succ
s Pitch O3 F
and blows up. How could it know that it should stop there?
From the Prelude documentation:
For any type that is an instance of class
Bounded
as well asEnum
, the following should hold:…
enumFrom
andenumFromThen
should be defined with an implicit bound, thus:enumFrom x = enumFromTo x maxBound enumFromThen x y = enumFromThenTo x y bound where bound | fromEnum y >= fromEnum x = maxBound | otherwise = minBound
Thus, to fix this problem, just add
instance Bounded Pitch where
minBound = Pitch minBound minBound
maxBound = Pitch maxBound maxBound
and then add the code from the documentation:
instance Enum Pitch where
-- ...
enumFrom x = enumFromTo x maxBound
enumFromThen x y = enumFromThenTo x y bound
where
bound | fromEnum y >= fromEnum x = maxBound
| otherwise = minBound
and now [Pitch O1 A ..] will stop at the end:
λ> [Pitch O1 A ..]
[Pitch O1 A,Pitch O1 B,Pitch O1 C,Pitch O1 D,Pitch O1 E,Pitch O1 F,Pitch O2 A,Pitch O2 B,Pitch O2 C,Pitch O2 D,Pitch O2 E,Pitch O2 F,Pitch O3 A,Pitch O3 B,Pitch O3 C,Pitch O3 D,Pitch O3 E,Pitch O3 F]
Side note: you can replace separate calls to div
and mod
with pattern-matching on a single call to divMod
: x `divMod` y == (x `div` y, x `mod` y)
. (For strictly positive numbers, like these, I believe I've heard that quotRem
may be a better choice; quot
and rem
are like div
and mod
, but with different sign-related behaviors.) Additionally, you could replace your 6
s with 1 + (fromEnum (maxBound :: Note))
to avoid accidentally getting the number wrong.
[x ..]
is desugared into enumFrom x
method, for which the prelude provides the following default implementation:
enumFrom x = map toEnum [fromEnum x ..]
Override it to do the right thing by your instance. You'll also likely want to override enumFromThen
for similar reasons.