Original-Via: uk.ac.nsf; Mon, 7 Oct 91 21:36:58 BST
| Date: Tue, 1 Oct 1991 17:46:05 +0100
| From: Mikael Rittri <[EMAIL PROTECTED]>
| Subject: Why can't Real be made a subclass of Enum?
|
| I tried to insert the Enum class (page 30) into the diagram
| of numeric classes (figure 7, page 55), but got confused.
|
| Every standard type in class Real is individually declared
| to be an instance of Enum, using the same functions
| (numericEnumFrom and numericEnumFromThen, page 90).
|
| Yet, Real is technically not a subclass of Enum.
| The declaration of Real (page 57 or 86) is
|
| class (Num a, Ord a) => Real a where
| toRational :: a -> Rational
|
| so I wonder why it isn't
|
| class (Num a, Ord a, Enum a) => Real a where
| toRational :: a -> Rational
| enumFrom = numericEnumFrom
| enumFromThen = numericEnumFromThen
It would be nice to be able to do this. Another case like this
is that Integral could be a subclass of Ix. But alas,
| I guess the answer is that class operations must either be defined
| at the topmost class (in this case Enum), or for each individual
| type, but not at an "intermediate" class (like Real).
We could still make Real a subclass of Enum, but that would merely
require Enum instances for each instance of Real, rather than
generating them automatically.
| In "The Next Stage" section, page vii, it says that in the
| future, a class declaration may include default methods for
| operations of its superclasses, which _override_ the superclass's
| default methods.
| But in the Enum/Real case, Enum has no default methods for
| enumFrom and enumFromThen, so no overriding is necessary.
That is, we could include the default methods
enumFrom = numericEnumFrom
enumFromThen = numericEnumFromThen
in the Enum class declaration, but I think this is bad form:
These methods use operations of class Real (and its superclasses)
What happens if some random, nonnumeric type is given an Enum
instance declaration without any method definitions?
| It would be interesting to see an explanation of the technical problems
| of having default definitions in an "intermediate" class declaration.
Yes, perhaps now is the time to discuss this. As far as I know,
the only difficulty with providing the generalization mentioned
in the "The Next Stage" section is the multiple inheritance problem:
A
/ \
B C
\ /
D
With this class inclusion structure, what default methods are available
to instances of class D if the declarations of classes B and C both
provide default methods for some operations of class A that the class
D declaration does not provide default methods for? One solution
is that D not inherit any default method where there is a conflict.
We could further require that D provide a default method in such a
case. Should there be a way for D to resolve a conflict by
specifying which method to inherit? If we have qualified names,
this could be done by allowing Class.method as a name.
I think we were wise not to open this can of worms in Haskell 1.0 or 1.1,
but I think it is worth doing. Let me close with one more application
of this idea:
I would like to define a subclass of Ord:
data Comparison = LT | EQ | GT
class (Ord a) => TotalOrd a where
compare:: a -> a -> Comparison
compare x y | x == y = EQ
| x < y = LT
| otherwise = GT
And here I can also override the slightly odd default methods for
min and max from class Ord, which assume a type with a preorder or
partial order, but not a total order:
min x y | x <= y = x
| otherwise = y
max x y | x >= y = x
| otherwise = y
(Actually, I would want to rename Ord PartialOrd and call the above
class Ord, and also provide a superclass of PartialOrd called Preord,
which would not be a subclass of Eq and would contain the operators
<= and >= but not < and >, but that's a slightly different subject.)
--Joe