FDs and confluence
claus.reinke at talk21.com
Wed Apr 12 20:47:57 EDT 2006
> Do you have a revised set of restrictions on the form of instances?
sure, but you won't like them any better now than the last time I
- as for confluence, none of the restrictions seem necessary
without FDs, type class inference proceeds by _matching_
of instance heads to constraints, and without overlapping
instances, there cannot be more than one matching head.
so the only divergence comes from reductions on separate
parts of the constraint store, which are independent, so the
divergence is temporary.
with FDs, type class inference adds _unification_, but only
for FD range parameters. instance heads that only differ in
FD range positions are now overlapping, but only as far
as improvement is concerned. note also that the resulting
overlap in the left hand side of rules is over variables, so
no variable instantiation can disable such rules. to be initially
diverging, such overlapping improvement rules must have
different unifications as their right hand sides. memoisation
ensures that any improvement that was possible at any
point in the reduction process remains possible throughout.
so following either of two conflicting improvements will
leave the other enabled, ensuring failure of the constraint
store instead of non-determistic results.
consistency condition: neither necessary nor sufficient
coverage/bound variable condition: as far as confluence is
concerned, these were possibly motivated by the wish
to have variable-restricted CHR, without which arbitrary
rule systems might introduce non-confluence by following
different instantiations for the fresh variables, through
unification with different left hand sides. but due to the
peculiarities of Haskell's type classes, such instantiations
do not appear to be a problem, as sketched above.
(as far as termination is concerned, I remain unconvinced
that these two rules are the right way to exclude problematic
cases, as they exclude many valid programs just because
these might be invoked with problematic queries. in such
cases, I'd prefer to exclude the problematic queries.)
early checking for superclass instances: also does not
seem to be needed any more; as the superclass constraints
will be introduced as proof obligations in the same step
that introduces infer_Class and memo_Class constraints,
they cannot be "forgotten", and inference cannot terminate
successfully if those instances are missing (they may, however,
be provided later than now, accepting more valid programs).
(sigh, I'm sure I've written this before, as so many other
things here.. I may well be wrong in what I'm saying - if that
is the case, please tell me so, but _please_ don't keep asking
me to repeat myself..)
- as for termination, the checks not related to the conditions above
are a useful start, but they are incomplete, and overly restrictive.
in fact, we know that we are dealing with an undecidable problem
here, so that no finite system of restrictions will be able to do the
job statically. we've already discussed various improvements to
the current set of checks here, as have others here and elsewhere.
my preference here is unchanged: the variant without static
termination checks needs to be the basis, and it needs to be
defined clearly, so that all implementations of Haskell' accept
the same programs and give the same results. the reason for
this preference is my opinion that Haskell has become too
complex a language already. heaping special cases on top
of restrictions that are necessarily incomplete adds
complexity to the language, while restricting its expressiveness.
on top of that basis, we should have termination conditions,
and we should keep trying to push the envelope by making
them encompass more valid programs, but that also means that
these termination conditions need to keep evolving! so we
should have warnings if the current set of termination checks
cannot statically determine whether or not some type class
program will terminate, and by all means, have a flag to decide
whether or not those warnings should become error messages
(for teaching contexts, or for any other software development
processes based on strict coding standard conformance, the
former might be the right default option).
but in the near future, I do not expect to see either of the
following two: a set of termination conditions that will cover
and permit all practically relevant programs, or a correct
and terminating, practically motivated type class program
that will run much longer than it takes me to read and
understand the warnings produced by the current set of
oh, two more things: if type class inference fails to terminate,
I never get to run my program, so that is perfectly type safe;
and if, as will eventually happen, part of my terminating type
class programs are beyond the abilities of the current set of
termination checks, I do not want to have to resort to a
module- or project-wide license to get my code through.
I'm no expert in this, but I believe that those folks who are
concerned with termination proofs accept it as a matter of
routine that tailoring a termination-ordering for each particular
problem is an important part of their business; but if Haskell'
does not support this, and still insists on doing mandatory
termination checks by default, it should at least allow me to
indicate which of the type classes/instances I want to take
responsibility for (via a pragma, most likely), using its checks
on all others.
More information about the Haskell-prime