Instance/given overlap trips up the ambiguity check
Thursday is a good day to abuse GHC. So I said this:
{-# LANGUAGE TypeFamilies, FlexibleInstances #-}
module Bug where
class IsBool bool
instance bool ~ Bool => IsBool bool
class C a
instance C Bool
f :: (C bool, IsBool bool) => ()
f = ()
To me, f
's type is unambiguous: the IsBool bool
constraint fixes the type variable bool
to be Bool
. But GHC says
/Users/rae/temp/Bug2.hs:11:6: error:
• Could not deduce (C bool0)
from the context: (C bool, IsBool bool)
bound by the type signature for:
f :: forall bool. (C bool, IsBool bool) => ()
at /Users/rae/temp/Bug2.hs:11:6-32
The type variable ‘bool0’ is ambiguous
• In the ambiguity check for ‘f’
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
In the type signature: f :: (C bool, IsBool bool) => ()
|
11 | f :: (C bool, IsBool bool) => ()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
But maybe I'm wrong. Is the type really ambiguous? No. When I say
{-# LANGUAGE TypeFamilies, FlexibleInstances, AllowAmbiguousTypes #-}
module Bug where
class IsBool bool
instance bool ~ Bool => IsBool bool
class C a
instance C Bool
f :: (C bool, IsBool bool) => ()
f = ()
g = f
my program is accepted. I've added AllowAmbiguousTypes
and the binding g = f
. That binding is accepted -- no type applications or other funny business. This suggests that f
's type is not actually ambiguous.
What's going on is an over-eager instance/given overlap; see Note [Instance and Given overlap]
in GHC.Tc.Solver.Interact
. That Note ends with
All of this is disgustingly delicate, so to discourage people from writing
simplifiable class givens, we warn about signatures that contain them;
see GHC.Tc.Validity Note [Simplifiable given constraints].
But I don't get the warning! Even in my second program, which is error-free. Hunting further into GHC.Tc.Validity
, I find this code:
checkSimplifiableClassConstraint env dflags ctxt cls tys
| not (wopt Opt_WarnSimplifiableClassConstraints dflags)
= return ()
| xopt LangExt.MonoLocalBinds dflags
= return ()
| ...
where the ...
includes generating a warning. Of course, I do have -XMonoLocalBinds
, as implied by -XTypeFamilies
. What happens when I disable this (with an explicit -XNoMonoLocalBinds
)?
/Users/rae/temp/Bug2.hs:11:6: warning: [-Wsimplifiable-class-constraints]
• The constraint ‘IsBool bool’ matches
instance (bool ~ Bool) => IsBool bool
-- Defined at /Users/rae/temp/Bug2.hs:6:10
This makes type inference for inner bindings fragile;
either use MonoLocalBinds, or simplify it using the instance
• In the type signature: f :: (C bool, IsBool bool) => ()
|
11 | f :: (C bool, IsBool bool) => ()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
Good. The warning fires.
My conclusions:
- My first program really ought to be accepted. The type is not ambiguous. I'm willing to concede this point to "disgustingly delicate", though, if we don't see an easy way to fix.
- It looks the warning should fire even when
-XMonoLocalBinds
is in effect. My program has no local generalization involved. How would users disable the warnings? By not writing a simplifiable Given.