Coq, unlike many others, accepts an optional explicit parameter,which can be used to indicate the decreasing structure of a fixpoint definition.
From Gallina specification, 1.3.4,
Fixpoint ident params {struct ident0 } : type0 := term0
defines the syntax. but from it, we've known that it must be an identifier, instead of a general measure.
However, in general, there are recursive functions, that the termination is not quite obvious,or it in fact is, but just difficult for the termination checker to find a decreasing structure. For example, following program interleaves two lists,
Fixpoint interleave (A : Set) (l1 l2 : list A) : list A :=
match l1 with
| [] => []
| h :: t => h :: interleave l2 t
end
This function clearly terminates, while Coq just couldn't figure it out. The reason is neither l1
nor l2
are decreasing every cycle. But what if we consider a measure, defined to be length l1 + length l2
? Then this measure clearly decreases every recursion.
So my question is, in the case of sophisticated situation, where code is not straightforward to be organized in a termination checkable way, how do you educate coq and convince it to accept the fixpoint definition?
You can use something called a
measure
instead of a structural argument for termination. For this, I believe you have to use theProgram Fixpoint
mechanism, which is a little involved and will make your proofs look uglier (because it generates a structural recursion out of the proof that you provide, so that the function you will actually use is not quite the function you wrote).Details here: https://coq.inria.fr/refman/program.html
It also seems like something called
Equations
can deal with measures? cf. http://mattam82.github.io/Coq-Equations/examples/RoseTree.html https://www.irif.fr/~sozeau/research/coq/equations.en.htmlYou have multiple options and all of them boil down to structural recursion in the end.
Preamble
Structural recursion
Sometimes you can reformulate your algorithm in a structurally recursive way:
Incidentally, in some cases you can use a trick with nested
fix
es -- see this definition of Ackermann function (it wouldn't work with justFixpoint
).Program Fixpoint
You can use
Program Fixpoint
mechanism which lets you write your program naturally and later prove that it always terminates.Function
Another option is to use the
Function
command which can be somewhat limited compared toProgram Fixpoint
. You can find out more about their differences here.Equations plugin
This is an external plugin which addresses many issues with defining functions in Coq, including dependent types and termination.
The code above works if you apply this fix.
Fix
/Fix_F_2
combinatorsYou can learn more about this (manual) approach if you follow the links from this question about
mergeSort
function. By the way, themergeSort
function can be defined without usingFix
if you apply the nestedfix
trick I mentioned earlier. Here is a solution which usesFix_F_2
combinator since we have two arguments and not one likemergeSort
:Evaluation tests
Exercise: what happens with this last check if you comment out
destruct_list
lemma?