[Haskell-cafe] Instance Chains selection by class membership [was: [ghc-proposals/cafe] Partially applied type families]
Anthony Clayden
anthony_clayden at clear.net.nz
Mon Jun 5 06:25:32 UTC 2017
> > On Sun Jun 4 13:11:47 UTC 2017, J. Garrett Morris wrote:
> > ...
> > You keep talking about guarding instances or equations.
> > Guards are equivalent to closed type families,
>
> I think not. They're somewhere between CTFs and Instance
> Chains.
> In that guarded instances are not closed.
> Which also appears to be your claim wrt Instance Chains.
>
A further difference is wrt class instances and FunDeps.
AFAICT, the closed class instances/Partiality paper
is expecting no FunDeps, but using Assoc types
within the class to achieve the same effect.
I agree with that long-term aim,
so the following applies to both
guarded class instances and guarded type instances.
The dreaded "Instances inconsistent with FunDeps" error
can be boiled down to:
> class TypeEq a b p | a b -> p
> instance TypeEq a a HTrue
> instance TypeEq a b HFalse
The check for FunDep consistency
unifies the class parameters on LHS of FunDep,
yielding subsititution { a ~ b }.
Then applies that substitution to rhs. yielding
{ HTrue ~ HFalse } doesn't hold, so inconsistency.
GHC allows you to put this instead:
> instance TypeEq a a HTrue -- same
> instance (p ~ HFalse) => TypeEq a b p
The consistency test compares { p ~ HTrue },
and apparently sees those as unifiable,
despite the (~ HFalse) constraint.
There are those [for example, Iavor],
who think this should still count as inconsistent.
So it works (and has consistently since 2004),
but seems iffy.
Furthermore the bare typevar `p` breaks the Coverage
Conditions,
(again despite the (~ HFalse) constraint grr!
So you need UndecidableInstances, which apparently
is not as benign as heretofore thought.
With InstanceGuards it goes like this:
class {-# INSTANCEGUARDS #-} TypeEq a b p | a b -> p
instance TypeEq a a HTrue -- same
instance TypeEq a b HFalse | a /~ b
Now the consistency check:
unifies the class parameters on lhs of FunDep,
yielding { a ~ b } as before;
then substitutes into the guard,
yielding { a /~ a } -- a contradiction.
So it doesn't need to check the consistency of the result.
Because the 'arguments' to the FunDep are apart.
Notice the side-benefit:
by putting explicit HFalse,
we haven't broken the Coverage Conditions,
so don't need UndecidableInstances.
The guarded type family goes likewise:
> type family {-# INSTANCEGUARDS #-} Equals a b :: Bool
> type instance Equals a a = True
> type instance Equals a b | a /~ b = False
AntC
More information about the Haskell-Cafe
mailing list