The base library and GHC 6.10
Claus Reinke
claus.reinke at talk21.com
Wed Sep 3 08:12:05 EDT 2008
> 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?
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".
Here's an example:
module A where
class A a where a :: a
module B where
import A
instance A Bool where a = True
module C where
import A
f :: A a => a
f = a
-- g :: Bool
-- g = a
In 'C', class 'A' and type 'Bool' are available, but the instance is not,
so delaying instance selection ('f') works, while forcing instance selection
('g') gives a "missing instance" error.
module D where
import A
import B
import C
g :: Bool
g = a
main = print (f::Bool,g)
In 'D', type, class, and instance are available, so 'g' is allowed here.
The word you are looking for is perhaps "accumulative": no module
provides fewer instances than its imports, so instances accumulate
recursively along the import hierarchy, no matter what the import/
export specifications say.
In Haskell 98, that means conflicting instances ought to raise an
error in 'Main' at the latest, but GHC deviates from that on purpose
(#2356), which makes the life of projects using multiple libraries a
little easier.
I have often wondered why Haskell doesn't provide control over
instance import/export, even without going all the way to naming
instances:
module B(instance A Bool)
or
import B hiding (instance A Bool)
should go quite a way to improving instance scope control. Is it
just that people want more (named instances), or is there any real
problem with this simple approach?
I'd prefer explicit instance scope control
import X hiding (instance C a) -- no 'C' instances from package 'x'
import Y (instance C a) -- all 'C' instances from package 'y'
over the current "you can have conflicting instances, as long as you
don't use them in conflicting ways" (#2356).
> That means, for the particular case of the Data class, someone should
> decide once and for all whether there is an instance for IO, or functions,
> or whatever, and either define them along with the Data class or not at all.
Ultimately, yes. And there have been several suggestions for
improving the instances, or limiting the class to what is implementable,
or avoiding the need for dummy instances to support deriving, etc. But
even in the final analysis, there may be no better option than to provide
dummy instances via explicit imports for those applications that want
them.
I mean I could decide that I'm in a "don't permit anything unsafe" mood
and try to force this instance into any importer of 'Data'
instance Fail a => Data (IO a)
(where 'Fail' is a class that cannot have instances, as guaranteed by
not exporting the class). Then I'd be happy until the day I actually need
an instance, any instance of 'Data (IO a)' (or someone else does;-).
The trick is to split the imperfect code we have in such a way that it
does not prevent users from taking their pick, and making their own
choices, suitable for their projects.
Anyway, there is no hope of deciding the fate of these instances in
time for the release candidate, and the decision to be made now is
how to support a realistic amount of improvement after the release,
by making suitable _preparations_ before the release.
Claus
More information about the Libraries
mailing list