
The question then comes down to whether that warning should ever be strengthened to an error.
Indeed.
I agree that such a scenario is possible. The present situation gives no choice but to do things badly, but things often get done badly the first time around anyway. Perhaps I'm just grumpy, but I think we should aim to make bad practice erroneous where practicable. Once the mistake is no longer forced upon us, it becomes a mistake that deserves its penalty in labour. Silent pre-emption is bad practice and code which relies on it should be fixed: it's not good to misconstrue an instance declaration because you don't know which instance declarations are somewhere else. Nonmonotonic reasoning is always a bit scary.
From a library design perspective, we should certainly try to get these hierarchical choices right when we add classes. I accept that it should be cheap to fix mistakes (especially when the mistake is lack of foresight. Sticking with the warning rather than the error reduces the price of this particular legacy fix at the cost of tolerating misleading code. I agree that the balance of this trade-off is with the warning, for the moment, but I expect it to shift over time towards the error. But if it's clear what the issue is, then we can at least keep it under review.
I agree. Making bad practice erroneous is good, but its not really the bad practice that raises the error here. You have no serious problems until you try to change your bad design to a good one. Like you say it should be cheap to fix mistakes.
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?
I don't know what you mean by this. Perhaps you could expand on it?
What I'm trying to ask is if you can write compatible code that will work across gradual changes of the compiler and the libraries. Suppose we have library with class C. In a newer version of the library we add an intrinsic superclass S. Also suppose the compiler implements option 1. Now the users of the library want to write code that uses both C and S, and that's compatible with both the new and the old library. From what I can tell there are three situations that needs to be covered: 1) Old compiler - Old library Here we need to specify both instances, and we cant hide the default S instance because its not supported by the compiler. This also applies for other situations where the client must use Haskell 2010 compatible code. 2) New compiler - Old library Here we also need to specify both instances. 3) New compiler - New library We can either write both instances and hide the default or we can just write an instance for C. Clearly code that covers situation 1 will never be compatible with situation 3. The question I was asking was if we are allowed to hide the default instance of S in situation 2. In that case you can write compatible code for situation 2 and 3. The possible confusion from this is that you hide a default implementation thats not defined. Maybe it's not as bad as overriding silently, but there is some room for error where you think you have blocked a superclass instance but really you have just blocked some completely unrelated class. Of course we can get compatibility across all three using CPP but I really wish we won't need that. As time passes, situation 1 will become more rare, although situation 2 and 3 can reoccur endlessly as new libraries are designed and redesigned. Regards, Jonas