
Claus Reinke wrote:
.., the only sensible way to think about instances is as global properties. ..it has nothing to do with bugs or misfeatures in GHC, it's a fact of Haskell 98.
I thought my example demonstrated quite clearly that instances are *not* global in Haskell.
Yes, I know that you can limit the visibilty of instances by using orphans in Haskell 98. My point was that it is a mistake to design libraries this way, because it leads to a loss of abstraction.
This is even worse at the level of packages. We can hide modules that are used internally to a package's implementation, but we can't hide the fact that a package used some non-standard instances internally, and furthermore we can't change this aspect of its implementation without changing the API.
How do packages make a difference here? As long as I don't import base:Control.Monad.Error, the base:Control.Monad.Instances instances of Functor, say, are not visible in base:Control.Monad.
If I write a package that uses Control.Monad.Instances internally, that becomes visible via its API, and I cannot abstract away from this aspect of the implementation of my package. This is an absolute failure of abstraction, and if we value abstraction (which we surely do?) then we should look for ways to fix the problem. So one way is to look at changing the language, perhaps by supporting explicit re-export of instances. Certainly that's a worthwhile direction to explore, but since our current language doesn't have this extension, we have to look for other ways to avoid the problem in the meantime. The only solution is to not have orphan instances in the original library API, which is what I'm arguing for. I think you misunderstood my point before (but you recognised that I repeated it :-). I'm not trying to say that Haskell 98 only supports global instances, rather I'm saying we should think of it that way and design our libraries with global instances in mind, because otherwise we have serious problems with abstraction. We agree that this is a failure of the language. You seem to be arguing that we should pretend that the language is not broken in the hope that it gets fixed in the future - I think that's the wrong approach.
Sadly, that has all been discussed to death already, and again, it is a matter of being precise. "Orphan" instances are not wrong per se - they encode and name the extent of type relations via modules, but one needs to think carefully about their intended use and whether that use is really supported by the language or just an illusion. Of the top of my head, I can think of two uses:
(a) having two instances of the same class for the same types in the same program only works by "virtue" of #2356, so should be avoided unless and until the positive aspects of #2356 are moved from accident to design decision
Right, agreed.
(b) giving clients control over which instances they want to use (eg, use set A or set B, or neither) should work, and mostly does, but may run into ghc #2182 and haddock #54. Also, it is advisable only for client applications, not for client libraries, as long as their users might run into unresolved aspect of (a).
I'm not sure what "client libraries" are, but I think I agree, if what you're saying is that orphan instances should be kept out of library APIs. I'd also argue that they shouldn't be used in application code either, but the reasons are less compelling there.
My preference would be to see ghc #2182, #2356 (for Haskell 98 mode) and haddock #54 fixed. #2356 (for Ghc mode) is documented behaviour, I believe, inherited from Ghc's handling of overlapping instances, but there is no LANGUAGE extension specifying this behaviour, so it isn't portable.
I'll just point out that if we stop using orphan instances in library APIs, then #2182 is much less of an issue.
Next, I'd like to see whether more control over instance re-export is permissible in theory and -if yes- would like to see it implemented and standardised.
Sure, please do! Cheers, Simon