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 succing the generated values until it succs 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 as Enum, the following should hold:

  • enumFrom and enumFromThen 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 6s 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.

Tags:

Haskell