
Hi,
On 31 August 2011 12:22, Conor McBride
I become perplexed very easily. I think we should warn whenever silent pre-emption (rather than explicit) hiding is used to suppress a default instance, because it is bad --- it makes the meaning of an instance declaration rather more context dependent. Perhaps a design principle should be that to understand an instance declaration, you need only know (in addition) the tower of class declaration s above it: it is subtle and worrying to make the meaning of one instance declaration depend on the presence or absence of others.
Those are all good arguments, and you've convinced me that always warning is better.
Arguably, option 1 does not conflict with design goal 1. Design goal 1 supports a methodology for refining a class into a hierarchy without creating the need for stacks of default instances or breaking code. If the new superclass is a brand new thing without legacy instances, there's no problem. If we'd had this mechanism in place, Functor would always have been made a superclass of Monad, Applicative would have been easily inserted, and we wouldn't have the stacks of manually added default instances to worry about.
The main problem with Option 1 is in dealing with the legacy of classes which currently require a stack of default instances, creating a hierarchy from parts which already exist. Option 1 would create a bunch of instance conflicts and thus demand changes to code. Design goal 1 isn't very explicit (sorry!) about this distinction between introducing new classes as superclasses and building hierarchies from legacy classes, but it was the former I intended. I always expected the latter to cause trouble.
If it is also a design goal to minimize damage with respect to the current unfortunate situation, then Option 1 is problematic. Whatever we might wish, we are where we are. We should be pragmatic. I think we should set Option 1 as the direction of travel, but go with Option 2 for the moment. We should make sure that the warnings generated by Option 2 are sufficiently informative that it is easy to track down the conflicts and resolve them explicitly, for Option 1 compliance.
First of all, I think the design goal is quite clear: "a class C can be re-factored into a class C with a superclass S, without disturbing any clients". Requiring client C to opt-out from the default implementation of S is a clear violation of the design goal. So I disagree that option 1 can be compatible with the design goal, but like you say the design goal might be at fault. Also, if I understand you correctly, you say the current situation is exceptional, and suggest option 2 as a temporary solution to it. You seem convinced that these kind of situations will not appear in the future, but I'm not as optimistic about that. Even when superclass defaults are implemented, people will occasionally implement classes without realizing that there is a suitable intrinsic superclass (or add the superclass but not the default instance). People will start using the new class and give separate instances for the superclass, and eventually someone will point out that the there should be a default instance for the superclass. Now if option 1 is implemented, the library maintainers will be reluctant to add the superclass instance because it will break a lot of client code. Will there be a solution to this dilemma that I have missed? Should the client code be allowed opt-out from the superclass preemptively before it is given a default? Won't that cause a similar perplexity? Regards, Jonas