The HList project (~2004) had to abandon Hugs because (for example) they couldn't express a type-level type equality test. Hugs rejects any attempt 'Instances are not consistent with Functional Dependencies'. Here's the equivalent that works in GHC.
class TypeEq a b e | a b -> e instance TypeEq a a TTrue instance {#- OVERLAPPABLE -#} (e ~ TFalse) => TypeEq a b e
Hugs rejects the equivalent of these instances because it can unify the instance heads' determining positions (as TypeEq a a e0) but the improvement diverges. Hugs always treats a wildcard e0 as apart from any specific type or from any other wildcard; whereas GHC treats them as unifiable, so accepts the instances. The instances (whole heads) are overlappable, so GHC's overlapping instances mechanism will improve e ~ TFalse only on condition it can't unify types a, b at some usage site. SPJ points out https://gitlab.haskell.org/ghc/ghc/-/wikis/Functional-dependencies-in-GHC/Ke... that using an instance head to improve under a FunDep is a separate step and proceeds first vs selecting an instance. So this form for the second instance won't work in GHC:
instance TypeEq a b TFalse
(Also the instance heads are not overlapping.) In case of TypeEq a a e0, instance improvement will find both instances match and improve e0 ~ TTrue, e0 ~ TFalse, then reject as inconsistent, same as Hugs. But notice this FunDep is 'Full' -- that is, mentions all of the classes, parameters. ('Full' FunDep is introduced in the 2008 'via CHRs' paper.) If improvement can use a Full FunDep matching to an instance head, it has in effect selected that instance. This is really using the instance as a _candidate_ for improvement -- it might be the Dependent types are already improved as far as possible before looking at this instance. (That is, if we already have [W] TypeEq Int Int TTrue, looking to the instance heads won't improve further; but Hugs is not smart enough to shortcut that TTrue can't be improved.) Hugs' type routines at this step of compilation are very much focussed on improving types, not selecting instances. And in case of a class with multiple FunDeps, the routines should 'fire' all possible FunDeps for each instance. However it's only a small mod in routine instImprove to say: upon matching to a Full FunDep for an instance, stop looking at other instances for improvement. A precondition is that it scans instances in most-specific-first sequence -- which Hugs is already doing. So I can write these instances
class TypeEq a b e | a b -> e instance TypeEq a a TTrue instance TypeEq a b TFalse
Without needing to clutter the instances by deferring improvement to constraints. (This also makes type inference error messages less cluttered.) I'm now working on the instance sequencing routines, to ignore those divergent Dependent positions when deciding overlap ordering. AntC