[Hugs-users] FunDeps: divergent improvement from overlapping instances

Anthony Clayden anthony.d.clayden at gmail.com
Sun Aug 7 10:14:12 UTC 2022


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/Key-examples/FunDep-improvement-vs-instance-selection#qs-for-spj
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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/hugs-users/attachments/20220807/e152c995/attachment.html>


More information about the Hugs-Users mailing list