Ian Holyer writes:
> To go back to the debate on instances, here is a concrete proposal for
> handling instances in Haskell 1.3:
I can see what you're doing, but I dislike the idea of no longer being
able to define instances local to a module. This limits my choice of
class and type names, and may cause problems when importing libraries
defined by other users. For global (exported) instances your rules
make sense (a variant of these was considered at one point) with the
caveats marked below.
> 1) A C-T instance can be defined in any module in which C and T are
> in scope.
Fine, in conjunction with 5 and 2 or similar constraints.
> 2) A C-T instance defined in module M is in scope in every module which
> imports from M, directly or indirectly. (If C or T are not in scope, a
> module just passes the instance on in its interface).
You need to ignore local C-T instances (i.e. those where a class C or
type T is defined locally and not exported), otherwise mayhem could
result. Local instances will now also cause problems if there is a
global C-T instance defined in any importing module.
The interface is problematic if a new class with local name C or type
with local type T is defined (or both!), especially if there is a
(local) C-T instance. Getting round this would involve being much more
explicit about global names in interface files (e.g. an M1.C-M2.T
instance). There is also potential name capture of type, class, or
operator names by the importing module, which would require
additional checking of interfaces import (something we would like to
avoid for efficiency reasons).
> 3) A C-T instance may be imported more than once via different routes,
> provided that the module of origin is the same.
This implies annotating instances with their module of origin, as
you note below.
> 4) If an application of an overloaded function is resolved locally, the
> relevant instance must be in scope.
...a relevant instance must be in scope...
> 5) There must be at most one C-T instance defined in the collection of
> modules which make up any one program (global resolution occurs in Main).
There should be at most one global C-T instance defined (otherwise you
lose the ability to create local types with instances)... You also
shouldn't specify where resolution takes place. Link resolution is
> I would like to see the origin of instances in interface files. My preference
> from an implementers point of view would be something like:
> interface M1 where interface M3 where
> import M2 (C(..)) or import M2 (C(..))
> import M3 (T(..),fT) type T = ...
> instance C T where f = fT instance C T where f = fT
> The name fT is invented while compiling M3 and passed around in interface
> files, but not exported from them into implementation modules. As well as
> specifying the origin of the instance, it gives the code generator something
> to link to.
This really isn't a problem for an implementation. We can always link to a
hidden name derived from the unique C-T combination. Introducing magic
names in an interface sounds like a *very bad* idea -- you might well
accidentally capture a user- or Prelude-defined name. For example,
class From where
from :: Int -> [a] -> a
instance From Int where
from = ...
introduces fromInt in the interface, which will clash with the Prelude
interface M1 where
import M4(instance M2.C M3.T)
is probably closer to what's required.