[Haskell-beginners] Why do class instance functions behave differently when executed directly in WinGHCi than when loaded from a file?

Daniel Fischer daniel.is.fischer at googlemail.com
Fri Jun 10 13:50:17 CEST 2011

On Friday 10 June 2011, 12:53:30, Costello, Roger L. wrote:
> Hi Folks,
> I created a file, and in the file I entered this type class and an
> instance:
>     class MyEqualityOperator a where
>         isEqual :: a -> a -> Bool
>     instance MyEqualityOperator Double where
>         isEqual x y = x == y
> To test the isEqual function I entered this:
>     test = isEqual  3 4
> When I loaded my file into WinGHCi, I got this error:
> ------------------------------------------------------------------------
> ---- Ambiguous type variable `t' in the constraints:
>       `Num t'
>         arising from the literal `3' at simpleTypeClassTest.hs:11:14
>       `MyEqualityOperator t'
>         arising from a use of `isEqual' at
> simpleTypeClassTest.hs:11:5-16 Probable fix: add a type signature that
> fixes these type variable(s)
> -----------------------------------------------------------------------
> -----
> Why is it "ambiguous"?

The end is "because the type checker works with an open-world assumption 
and hence doesn't use the instances in scope to determine the type".

Before that comes
- numeric literals are polymorphic, the literal 3 stand for (fromInteger 3) 
and has type (Num t) => t

So the type checker sees

3 :: Num t => t
4 :: Num n => n

Those are arguments to

isEqual :: MyEqualityOperator a => a -> a -> Bool

hence n = t, but the type checker still doesn't know which type that is, it 
only has the constraint

3 :: (Num t, MyEqualityOperator t) => t

(and 4 has the same type).

Now, that's harmless so far, but test = isEqual 3 4 now gets the type

test :: (Num t, MyEqualityOperator t) => Bool

and here you have a type variable, t, in the constraints which doesn't 
appear on the right hand side, thus that is an ambiguous type.

Now, if the type checker took the instances in scope into account, it could 
find that the only instance of MyEqualityOperator in scope is for Double, 
resove t to Double and be done with it.

But, then somebody imports that module in a module where also an

instance MyEqualityOperator Int where...

is in scope, and test now becomes unresolvable - that's why the type 
checker cannot use the instances in scope to determine the types involved, 
things might be imported in environments with other instances in scope and 
that would break stuff.

Now, if you had

test2 = 3 == 4

in your source file, that would compile without an ambiguous type variable 

But that's almost the same as test, the only difference is that the 
inferred type for test2 is

test2 :: (Num t, Eq t) => Bool

What's going on?

Under certain circumstances, such ambiguous type variables are resoved by 
defaulting (cf. section 4.3.4 [iirc] of the language report).
The conditions are
- all constraints are of the form (Cls v)
- at least one of the constraints involves a numeric class
- all involved classes are defined in the Prelude or the standard libraries

These conditions are fulfilled for test2, so there the type variable t gets 
defaulted to Integer [unless you have a default declaration in your module 
overriding the default default].

For test, the third condition is not fulfilled, so the ambiguity does not 
get resolved by defaulting.

> Interestingly, when I typed this at the WinGHCi command prompt:
>     isEqual  3 4
> I got no error and I got the expected result (False).
> Why is this?

Because ghci uses extended defaulting rules.
At the prompt, there's less context to determine type variables than in 
code, so ambiguous situations arise more often. To reduce the frequency of 
ambiguous type errors, ghci uses defaulting also in situations not covered 
by the report.

> What would I need to do, in my file, to make this work properly:
>     test = isEqual  3 4

Provide a type annotation,

test = isEqual 3 (4 :: Double)

> /Roger

More information about the Beginners mailing list