> | > > class (Eq key, Ord key) => Dictionary dict key dat where
> | > >      delete :: key -> dict -> dict
> | ...
> | > the first error:
> | > 
> | >     Class type variable `dat' does not appear in method signature
> | >         delete :: key -> dict -> dict
> | > 
> | > Why does ghc expect that I use all of the type variables?
> | > Obviously I only need
> | > the key to remove an entry of a dictionary.
> | 
> | You're right.  The restriction is excessive.  Thanks for pointing
> | this out.  Probably we should only require that at least one
> | of the class variables is constrained.
> 
> I don't see this.  The fact is that, if any of the variables in the
> class header don't appear in the type of a method, then that method
> will always have an ambiguous type.  In this case, that would be:
> 
>    delete :: (Dictionary dict key dat) => key -> dict -> dict

It would not *always* result in ambiguity. For example, consider

        instance Dictionary (MyDict dat) Int dat where ...

        f :: MyDict dat -> MyDit dat
        f d = delete (3::Int) d

Here, the polymorphism in 'dat' doesn't affect which instance
is chosen.

However, on further reflection, I now think:

        in any case where ambiguity would not result
        the original class declaration should be re-formulated
        
So, I now think that the existing rule (all class variables
must appear in each class-operation type signature) is probably
the right one, but on stylistic rather than technical grounds.

> I don't see how Simon's suggestion of replacing "all" with "at least one"
> could be made to work in general.  Such an approach is however possible
> if you are working with multiple parameter classes where the variables in
> one parameter are determined directly by the parameters in another.
> For example, consider the following class:
> 
>    class Collection elem col where
>        empty :: col
>        add   :: elem -> col -> col
>        del   :: elem -> col -> col
>        enum  :: col -> [elem]
> 
> By the argument above, one should expect empty to be rejected as having
> an ambiguous type:  Collection elem col => col.   However, we could also
> imagine modifying the definition of ambiguity to take account of the fact
> that, in practice, the value of elem would probably be uniquely determined
> by the value of col, and hence the context in which empty is used could
> still provide enough information to allow the overloading to be resolved.

Indeed. Consider

        instance Collection a MyColl where
           empty = MyEmpty
           ...

But this would be nearly useless, since we then know nothing
about the type elem.

So, again, nothing seems to be gained by relaxing the restriction
(unlike what I said yesterday).

Simon


Reply via email to