[GHC] #9210: "overlapping instances" through FunctionalDependencies
GHC
ghc-devs at haskell.org
Mon Jun 16 15:55:46 UTC 2014
#9210: "overlapping instances" through FunctionalDependencies
--------------------------------------------+------------------------------
Reporter: rwbarton | Owner:
Type: bug | Status: new
Priority: normal | Milestone:
Component: Compiler (Type checker) | Version: 7.8.2
Resolution: | Keywords:
Operating System: Unknown/Multiple | Architecture:
Type of failure: None/Unknown | Unknown/Multiple
Test Case: | Difficulty: Unknown
Blocking: | Blocked By:
| Related Tickets:
--------------------------------------------+------------------------------
Changes (by simonpj):
* cc: dimitris@…, diatchki (added)
Comment:
I can at least explain what is going on. I defer to Iavor (the Functional
Dependency Expert) on what the right thing to do might be.
== Problem 1: Should these instance declarations be accepted at all? ==
After all, unifying the a,b,s components of the instances shows that the
constraint `Foo (x,x) ?? x y` would match both instances, and hence (via
the fundep) force `??` to be both `(y,x)` and `(x,y)` respectively.
GHC doesn't currently complain about this, I think because there are some
yet-more-specific constraints that would not give rise to a conflict. For
example, suppose I was trying to solve `(Foo (Int,Int) ?? Int Int)`. Then
both fundeps would compatibly force `??` to be `(Int,Int)`. Mind you,
then the overlap checker would say that it couldn't pick which of the two
instances to use.
So my instinct is that this check for "yet-more-specific" constraints is
wrong. If the things on the LHS of the fundep arrow unify, then the
things on the RHS should be equal. That would reject these two instance
declarations. Iavor?
For completeness, the offending bit of code is this, in `FunDeps.lhs` line
318
{{{
Just subst | isJust (tcUnifyTys bind_fn rtys1' rtys2')
-- Don't include any equations that already hold.
-- Reason: then we know if any actual improvement
has happened,
-- in which case we need to iterate the
solver
-- In making this check we must taking account of
the fact that any
-- qtvs that aren't already instantiated can be
instantiated to anything
-- at all
}}}
== Problem 2: why does type checking succeed? ==
The second mystery is this: why is the constraint `(Foo (Int,Int) alpha
Int String)`, which is what arises from `main`, solved without error?
`alpha` is a unification variable. What happens is this.
* The constraint gives rise to two derived constraints, one from each
fundep `[D] alpha ~ (Int,String)` and `[D] alpha ~ (String,Int)`.
* The first is solved by `alpha := (Int,String)`.
* Having made that choice, the constraint `Foo (Int,Int) (Int,String) Int
String` uniquely matches the second instance declaration, and so can be
solved.
* The second derived constraint becomes `[D] (Int,String) ~ (String,Int)`
and therefore leads to two isoluble derived constraints `[D] Int ~ String`
and `[D] String ~ Int`.
* But if we manage to solve everything else, we discard insoluble derived
constraints; see `Note [Dropping derived constraints]` in `TcRnTypes`.
As a result of all this, we (totally bogusly) pick one instance
declaration, ignoring the fact that the other matches too. Ugh!
I'm not quite sure what to do, but let's sort out (1) before thinking
about (2). Iavor?
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/9210#comment:3>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list