Superclass defaults

Conor McBride conor at strictlypositive.org
Fri Sep 2 19:29:55 CEST 2011


Hi

On 2 Sep 2011, at 16:34, Brandon Allbery wrote:

> I hope I am misunderstanding this....


I wrote:
> 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

with the response:
> So, when the whole point is that an unfortunate design years ago  
> can't be reasonably fixed without rewriting massive amounts of code,  
> the only correct answer is to rewrite massive amounts of code?

I'm not sure what you're asking here. Of course we should compare the
pain of the treatment with that of the symptoms.

>  Especially when the original proposal was put forward *specifically  
> to avoid* rewriting massive amounts of code?

Which original proposal? How does it avoid rewriting code?

> Yes,  we'd love a perfect world.  We don't have one.  That's the  
> *point*.

Recall that Option 2 resolves the duplicate superclass instances in  
favour
of an explicit prior instance, but issues a warning (which should  
offer the
data to choose between explicit resolutions). That deals with a chunk of
the legacy scenario (although it doesn't handle the situation where  
some M
is made a Monad in one module and made Applicative in a later module,  
which
is possible (common, even?) because Applicative is not currently a
superclass of Monad). If we make one existing class a superclass of  
another
existing class, some disruption is inevitable: we can try to minimize  
that
disruption, but we can't eliminate it entirely. For another example,  
if some
F is made Applicative and Traversable in the same module, which  
default Functor
instance pre-empts the other?

We should question whether the disruption of even Option 2/3 makes it  
worth
adding default superclass instances at all. Maybe, depressingly, we've  
reached
the can't-fix-it stage. It would be good to get some data. It's also  
worth
considering tools to support migration, using the diagnostics  
generated by
warnings.

If it is worth adding default superclass instances, Option 2 looks  
like a
crucial disruption-minimizing expedience, while we have a legacy of  
newly
extraneous instances to deal with. As far as "an unfortunate design  
years
ago" is concerned, we should be careful to minimize the amount of  
rewriting
required. If that minimum is still too much, we'd better not go there.

I'm in favour of moving to Option 1 eventually, as somehow the better  
choice
for code comprehension. But I can see reasons to resist the changeover:

   * too much unmigrated code still relying on pre-emption under  
Option 2;

   * new instances of the old problem (an existing S is suddenly made a
       superclass of an existing C, with a default S instance for C)  
come
       into being,

The former is a real risk, but hopefully with a finite lifespan. It  
would be
too costly to switch from a warning to an error while too much code  
relies on
the deprecated practice. Please don't imagine I'm in favour of that.

The latter, however, requires us to be a bit dim in a way which was  
certainly
not in evidence when most of the current motivating examples arose. In  
the
legacy code (Monad-Applicative-Functor, Traversable-Foldable-Functor),  
we've
had to choose between two bad options, but the candidate
superclass-with-default-implementation has usually been evident. I'm  
sure
we're capable of being that dim. I'm also sure we're capable of  
screwing up
by writing an instance and assuming we get the default superclass  
instance
we expect, without noticing that someone else's chunk of the codebase
pre-empts it. I'd be troubled if someone knackered my applicative- 
style use
of Monad [] by adding a zipping Applicative [], or even an instance  
which
appeared to have the same functionality but also did some sneaky
unsafePerformIO badness. That's an example of the risk we take by  
allowing
pre-emption. We have to balance the risk of going back and resolving
duplicates with the risk of bugs caused by code meaning less than it  
says.

So, are default superclass instances just too disruptive?

All the best

Conor

PS We'd love a perfect world. We don't have one. That's why we change  
things.




More information about the Glasgow-haskell-users mailing list