As @ carsten-könig recommended, the solution would be to create a newtype wrapper for Char . This is not a workaround, but the right and really good way to avoid a whole class of problems associated with orphaned instances (instances for type classes that are defined in another module). Read more about these issues.
In addition, this approach is widely used when there are several possible cases with different behaviors.
For example, consider the Monoid class, which is defined in Data.Monoid as:
class Monoid a where mempty :: a
As you already know, Monoid is a type of value that can be added to each other (using mappend ) and for which there is an "identity" mempty that satisfies the mappend mempty a == a rule mappend mempty a == a (adding identity to the value of a leads to a ) There is an obvious Monoid instance for lists:
class Monoid [a] where mempty = [] mappend = (++)
It is also easy to define Int s. Indeed, integers with the addition operation form a regular monoid.
class Monoid Int where mempty = 0 mappend = (+)
But is this the only possible monoid for integers? Of course, this is not so, multiplication by integers forms another regular monoid:
class Monoid Int where mempty = 1 mappend = (*)
Both instances are correct, but now we have a problem: if you try to evaluate 1 `mappend` 2 , there is no way to figure out which instance should be used.
This is why Data.Monoid wraps instances of numbers in newtype wrappers, namely Sum and Product .
Go ahead, your statement
instance Arbitrary Char where arbitrary = choose ('0', '9')
can be very confusing. It says: "I am an arbitrary character," but gives out only numbers. In my opinion, this would be much better:
newtype DigitChar = DigitChar Char deriving (Eq, Show) instance Arbitrary DigitChar where arbitrary = fmap DigitChar (choose ('0', '9'))
Piece of cake. You can go further and hide the DigitChar constructor by providing the DigitChar “smart constructor” that will not allow you to create a DigitChar , which is not really a digit.
On your question, “Do you know why this is not the approach that took the lesson?”, I think the reason is simple, the tutorial seems to have been written in 2006, and in those days quickcheck just didn't define an Arbitrary instance for Char . Thus, the code in the textbook was completely correct in those days.