Haskell Enumeration

I am trying to write an instance of Enum for the following type:

-- Type declarations: -- Octave data Octave = O1 | O2 | O3 deriving (Show, Read, Eq, Ord, Bounded, Enum) -- Note data Note = A | B | C | D | E | F deriving (Show, Read, Eq, Ord, Bounded, Enum) -- Pitch data Pitch = Pitch Octave Note deriving (Show, Eq, Ord) -- Why doesn't this work? instance Enum Pitch where fromEnum (Pitch on) = (fromEnum o)*6 + (fromEnum n) toEnum x = (Pitch on) where o = toEnum (x `div` 6) n = toEnum (x `mod` 6) 

This is great for:

 [(Pitch O1 A) .. (Pitch O3 F)] 

But it doesn’t work:

 [(Pitch O1 A) .. ] 

With an error:

 *** Exception: toEnum{Octave}: tag (3) is outside of enumeration range (0,2) 

I understand the error. My questions: How to properly write an Enum instance to perform this enumeration? Is it possible? Most of all: is this a good practice?

+8
haskell
source share
2 answers

Your problem is that Pitch implicitly limited, i.e. has the smallest and largest element, but you do not reflect this limitation in your code. The code

 [Pitch O1 A ..] 

desugars to

 enumFrom (Pitch O1 A) 

which saves the succ generated values ​​until there is a succ Pitch O3 F and explodes. How could he know that he should stop there?

From the Prelude documentation :

For any type that is an instance of the Bounded class, as well as Enum , the following should be done:

...

  • enumFrom and enumFromThen should be defined using an implied boundary, thus:

     enumFrom x = enumFromTo x maxBound enumFromThen xy = enumFromThenTo xy bound where bound | fromEnum y >= fromEnum x = maxBound | otherwise = minBound 

So, 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 xy = enumFromThenTo xy 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] 

Note: you can replace individual calls with div and mod to match the pattern with one call to divMod : x `divMod` y == (x `div` y, x `mod` y) . (For strictly positive numbers like these, I think I heard that quotRem might be the best choice; quot and rem are similar to div and mod , but with different registration-related behaviors.) Alternatively, you can replace 6 with 1 + (fromEnum (maxBound :: Note)) , so as not to accidentally make a mistake.

+9
source share

[x ..] is output to the enumFrom x method, for which the preamble provides the following default implementation :

 enumFrom x = map toEnum [fromEnum x ..] 

Reinstall it to make the right thing your instance. You probably also want to override enumFromThen for the same reasons.

+5
source share

All Articles