[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
error.
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