[Haskell-cafe] (Feeling that) GHCi is trying to protect me from myself with overlapping instances and existential types
Juan Casanova
juan.casanova at ed.ac.uk
Sun Feb 16 15:39:24 UTC 2020
Please keep the discussion coming. The fact that I'm not convinced
(for now) doesn't mean I don't appreciate your answers. I wouldn't
want to come out as hostile. I'd be very happy for you to convince me
that I am understanding something the wrong way and learn how to do
what I want in the right way, which is what has happened essentially
every time I had a similar issue like this one. But until I am
convinced please allow me to argue back.
> Didn't Arjen already provide such an example? Should `g "Hello"`
> return a `Left` or a `Right`?
>
> g :: String -> Either String String
> g x = foo x
>
The g you are defining there should not compile indeed, because that
is an ambiguous usage of foo. But that is not what I defined. And this
relates to the rest of your reply.
> I think the difficulty may be due to the error being flagged in a
> misleading way. There's nothing wrong with your intended usage of the
> type classes, individually. The problem is that the instances both
> exist in the same codebase. The error message about overlap *appears*
> at the use sites but I believe that this is an implementation detail.
>
> The instances you have written can cause problems in *other* parts of
> the codebase, perhaps completely unrelated parts that have merely
> transitively imported your module through dependencies (someone may
> define `g` above, for example). Thefore the instances should not be
> both allowed to exist in the same codebase. This particular piece of
> information is flagged at the use sites but perhaps should be flagged
> at the instance definition sites.
I simply do not agree with this, and I don't think (?) that GHCi
implementors and designers agree with you either, as exemplified by
the fact that indeed overlapping instances appear when using instances
and not when defining them. While it doesn't clearly agree with what I
said, the general phrasing and approach followed here:
https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#instance-overlap makes me think indeed the approach is that potentially overlapping instances are allowed so long as each of their uses are
unambiguous.
To address your point more specifically, you worry that if potentially
overlapping instances exist, then someone somewhere else in the
codebase might flag up the overlapping instances error. But in order
for someone to do so, they need to *use* an instance of the class that
has the potentially overlapping instances. In your example, they need
to implement g and use foo within its definition, which means that
they are willfully utilizing a Class1 instance that they are thinking
of. If there are potentially overlapping instances that match, then
they need to take care that they use the one they really want.
Now, it is true that a good argument is that, for example in the g
example you presented, it is quite hard for the implementor to
actually indicate which instance they wish to use, since in this case
type annotations will not do the job. This is a problem I have also
had to deal with myself, and generally solve on a case by case basis
with various tricks and hacks (normally involving wrapping things in
newtypes). In my application case for this problem we are discussing
now, the newtype wrapping would in principle work, but would make my
code extremely more complicated due to the amount of type classes that
use each other that I am considering here and the fact that they are
multi-parameter type classes. All in all I am not very happy anytime
the solution to trying to do something legitimate is "wrap everything
up in newtypes and coerce stuff all over the place until it works". I
think it is too rampant and clutters the code way too much.
Perhaps the fact that no tools are given in GHCi to explicitly
indicate which instance we wish to use is an argument in favour of
your perspective that instances should not overlap even on their
definition? Maybe someone reading this who's more closely situated to
the Haskell / GHCi development and design principles could throw some
light on this? I don't feel like it is made clear on any of the
documentation I've read, but I may have overlooked it.
Rest assured, if it is like this by design as you claim it may be, I
would have a big amount of complaints about that as well. Things that
are absolutely sensible to do that would be made very noticeably
harder to do by this design decision. But it would certainly address
this particular issue.
Juan.
--
The University of Edinburgh is a charitable body, registered in
Scotland, with registration number SC005336.
More information about the Haskell-Cafe
mailing list