fundeps known beforehand (was RE: fundeps for extended Monad definition)

Hal Daume III hdaume@ISI.EDU
Thu, 27 Feb 2003 10:07:56 -0800 (PST)


Hi Simon, all,

> Here's a less complicated variant of the same problem:
> 
> 	class C a b | a -> b where {}
> 
> 	instance C Int Int where {}
> 
> 	f :: (C Int b) => Int -> b
> 	f x = x

This is interesting, but I'm not entirely sure what the problem is (from a
theoretical, not practical, standpoint).  Obviously there is no problem in
a one module program, so let's assume we have the following modules:

  module C where
    class C a b | a -> b where {}

  module I1 where
    import C
    instance C Int Int where {}

  module I2 where
    import C
    instance C Int Bool where {}

  module U where
    import C
    import ?
    f :: C Int b => Int -> b
    f x = x

Now, there are four cases:

  1) U (transitively) imports I1
  2) U (transitively) imports I2
  3) U (transitively) imports both I1 and I2
  4) U (transitively) imports neither I1 nor I2

Suppose we are in case 1.  Then the programmer has written a too-general
type signature on f.  The programmer *must* know that b=Int in this case
otherwise his function definition makes no sense.  However, I don't really
see a reason why this should be an error rather than just a warning.  If
some other module K imports both U and (for instance) I2, then we'll get a
fundep clash and the whole program will be invalid.

Suppose we are in case 2.  Then the programmer has clearly screwed up
because we know b=Bool and x::Int and x::Bool are incompatible.  This
should clearly be a type error.

Case 3 is impossible, as this will be a fundep clash.

Case 4 should produce an error (IMO).  Of course, there could be a module
K which imports this U and also I1 in which case you might argue that this
should be allowed, but I don't think that makes much sense.

> I suppose I could use (unsafeCoerce x) as the RHS, which amounts to
> saying "in every call to f, b will be Int, so we know that the coercion
> is safe".  But that is a scarily global property.  For example, if a
> call site of f does not "see" the instance declaration, it might call f
> with an argument of type (C Int Bool) or something.  Nor can I see an
> easy way to insert the 'right' coercions in general.

Is this possible?  In order for this to happen, this evil module M must
import U.  This means that it has transitively imported I1 and thus has
the instance declaration.  Or am I missing something.  I think the
unsafeCoerce x is safe in these cases.

> Bottom line: excellent point, but I can't see how to fix it.  Maybe
> there are some fundep experts out there who can guide us through the
> swamp?  

Yes, please!

 - Hal