
Marcin 'Qrczak' Kowalczyk wrote:
Wed, 14 Feb 2001 23:27:55 -0600, Matt Harden
pisze: I also wonder: should one be allowed to create new superclasses of an existing class without updating the original class's definition?
It would not buy anything. You could not make use of the superclass in default definitions anyway (because they are already written).
But that's not the point. The point is you could create objects that were only instances of the new superclass and not of the subclass. It allows us to have hidden superclasses of Num that wouldn't even have to be referenced in the standard Prelude, for instance. It allows users to define (+) for a type without defining (*), by creating an appropriate superclass of Num. We could keep the current Prelude while allowing numerous "Geek Preludes" that could coexist with the std one (at least with regard to this particular issue).
And what would happen to types which are instances of the subclass but not of the new superclass?
They would automatically be instances of the new superclass. Why not? They already have all the appropriate functions defined. Again, I wouldn't allow default definitions for the same function in multiple classes, and this is one of the reasons. It would introduce ambiguity when a type that is an instance of a subclass, and didn't override the default, was considered as an instance of the superclass.
Also, should the subclass be able to create new default definitions for functions in the superclasses?
I hope the system can be designed such that it can.
Me too :).
such defaults would only be legal if the superclass did not define a default for the same function.
Not necessarily. For example (^) in Num (of the revised Prelude) has a default definition, but Fractional gives the opportunity to have better (^) defined in terms of other methods. When a type is an instance of Fractional, it should always have the Fractional's (^) in practice. When not, Num's (^) is always appropriate.
I had many cases like this when trying to design a container class system. It's typical that a more specialized class has something generic as a superclass, and that a more generic function can easily be expressed in terms of specialized functions (but not vice versa). It follows that many kinds of types have the same written definition for a method, which cannot be put in the default definition in the class because it needs a more specialized context.
It would be very convenient to be able to do that, but it cannot be very clear design. It relies on the absence of an instance, a negative constraint. Hopefully it will be OK, since it's determined once for a type - it's not a systematic way of parametrizing code over negative constrained types, which would break the principle that additional instances are harmless to old code.
What happens if classes A and B are superclasses of C, all three define a default for function foo, and we have a type that's an instance of A and B, but not C, which doesn't override foo? Which default do we use? It's not only a problem for the compiler to figure out, it also quickly becomes confusing to the programmer. I'd rather just make the simple rule of a single default per function. If multiple "standard" definitions for a function make sense, then be explicit about which one you want for each type; i.e.: instance Fractional MyFraction where (^) = fractionalPow
This design does have some problems. For example what if there are two subclasses which define the default method in an incompatible ways. We should design the system such that adding a non-conflicting instance does not break previously written code. It must be resolved once per module, probably complaining about the ambiguity (ugh!), but once the instance is generated, it's cast in stone for this type.
Yeah, ugh. I hate having opportunities for ambiguity. Simple rules and obvious results are far better, IMHO.
What do you mean by mutual definitions? (snipped explanation of mutual definitions)
OK, that's what I thought :). I didn't really think this was of particular importance with allowing the definition of superclass's instances in subclasses, but now I think I see why you said that. It would be easy to forget to define one of the functions if the defaults are way up the hierarchy in one of the superclasses. Btw, I'm one of those who agrees that omitting a definition of a class function in an instance should be an error. If you really intend to omit the implementation of a function without a default, define it as (error "Intentionally omitted")! Matt Harden