While working through exercises in the Isabelle tutorials, I encountered a situation that confuses me. Why is it that the below lemma involving prepending to a list is proved so easily:
lemma ‹count_list xs x = n ⟹ count_list (x # xs) x = Suc n›
by simp
While this one involving appending is not?
lemma ‹count_list xs x = n ⟹ count_list (xs @ [x]) x = Suc n›
apply auto (* just does some minor rewriting *)
(* What should be go here? try/try0 are of no help. *)
done
I assume this has something to do with how the implementation of @
is more involved than #
due to the internal implementation of lists (#
is just another constructor while @
involves recursion), but what makes it so much more difficult to prove with and how do you account for that?
This has nothing to do with the ‘implementation’ of
@
. It has something to do with the ‘implementation’ ofcount_list
(but I would say ‘definition’ rather than ‘implementation’).Like most functions on lists,
count_list
is defined recursively (using theprimrec
command), which generates rules calledcount_list.simps
that are added to the simpset (the set of rules that the simplifier uses for rewriting automatically).If you type in
thm count_list.simps
, you will probably get something like this:Since they are in the simpset, the simplifier that is invoked as a part of
auto
will always rewrite any instance of the left-hand side to the right-hand side.Therefore, the first theorem you gave can be proven by a single step of rewriting with the second of these two rules and some more simplification. On that note, an equivalent and more usable formulation of that theorem would be
count_list (x # xs) x = Suc (count_list xs x)
.Similarly, I would very much suggest to reformulate your second lemma as
count_list (xs @ [x]) x = Suc (count_list xs x)
.Now why does
auto
not solve this? First of all, for this kind of goal, the only relevant part ofauto
for this kind of goal is the simplifier, so you might as well have writtensimp_all
here. And the reason why that cannot solve the goal is that there it has no simplification rule for something likecount_list (… @ …)
. You will have to prove this yourself.When you want to prove a property of a recursively-defined function, the usual approach is to do an induction that mirrors the recursive definition:
Or more compactly:
But I would actually recommend the following more general lemma:
In fact, it is reasonable to give this fact a name and add it to the simp set as it makes a good rewrite rule:
In case you're wondering why I did induction on
xs
here and not onys
: since the recursive definition ofcount_list
pattern-matches on the first list element and the remaining list, one must do induction on the first list (i.e.xs
) so that the induction step talks about(x # xs) @ ys
, which simplifies tox # (xs @ ys)
and then one can apply the induction hypothesis.Note that
simp_all
(orauto
) were sufficient to prove the induction goals in the above proof; once you tell Isabelle to do induction and what kind of induction, the rest of the proof is again just simple rewriting.