
(sorry for the delay in replying to this...) Claus Reinke wrote:
This raises the more general issue of instance-visibility. Since instances are automatically re-exported and therefore break abstraction barriers, the only sensible way to think about instances is as global properties.
I've heard this fatalistic view expressed before, and even for impoverished Haskell 98, it just isn't true. Perhaps it has come about from years of being bitten by either #2182, by attempts to avoid "orphan" instances, by carelessly designed libraries, or by careless instance imports, all of which make combining libraries that provide instances of the same class for the same type a pain?
No, it has nothing to do with bugs or misfeatures in GHC, it's a fact of Haskell 98.
A type class specifies a relation between types. Both the types and the class are named, and if instances are placed in separate modules, the modules are named as well. The combination of module, class and type names gives quite a bit of control over instance import/export, even if it is terribly cumbersome and limited (and easily defeated by just one library importing all instances "for convenience"). Neither the relation (class), nor its domain (types), nor its extent (instances) are "global".
The point is that instances are unconditionally re-exported, which exposes knowledge about the import structure underneath a module. If we consider instances to be part of the API of a module, then the API of a module is changed simply by changing what is imported. This is clarly an abstraction failure, because we want a module to be able to control its API independently of its implementation. 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. So thinking of instances as part of the API of a module is wrong, because it leads to the aforementioned abstraction failures. The only sensible way to think of instances is as global properties - one instance per class/type pair. Orphan instances are usually wrong unless the orphans are also exported via the standard API for either the class or the type. That is, orphans are ok in the implementation of a package, but not in the exposed API, because that makes it possible for a client to import both the class and type without getting the instance, which is what we have to avoid. Cheers, Simon