I have the following Isabelle goal:
lemma "⟦ if foo then a ≠ a else b ≠ b ⟧ ⟹ False"
None of the tactics simp
, fast
, clarsimp
, blast
, fastforce
, etc. make any progress on the goal, despite it being quite simple.
Why doesn't Isabelle just simplify the body of the if
construct so that both "a ≠ a" and "b ≠ b" become False
, and hence solve the goal?
The
if_weak_cong
congruence ruleBy default, Isabelle includes a set of "congruence rules" that affect where simplification takes place. In particular, a default congruence rule is
if_weak_cong
, as follows:This congruence rule tells the simplifier to simplify the condition of the
if
statement (theb = c
) but never attempt to simplify the body of theif
statement.You can either disable the congruence rule using:
or override it with an alternative (more powerful) congruence rule:
Both of these will solve the above lemma.
Why
if_weak_cong
is in the default cong setAnother reasonable question might be: "Why would
if_weak_cong
be in the default congruence set if it causes problems like the above?"One motivation is to prevent the simplifier from unfolding a recursive function infinitely, such as in the following case:
in this case,
solves the goal, while
sends the simplifier into a loop, because the right-hand-side of the
fact
definition continually unfolds.This second scenario tends to occur more frequently than the scenario in the original question, which motivates
if_weak_cong
being the default.Split rules
Besides case analysis and congruence rules, there is a third way to solve this goal with the simplifier: The splitter. The splitter allows the simplifier to perform a limited form of case analysis on its own. It is only run when the term cannot be simplified any further on its own (splitting cases can easily lead to an explosion of the goal).
The lemma
split_if_asm
instructs the splitter to split anif
in the assumptions:A single-step split can be performed with the
split
method:Note that the rule to split an
if
in the conclusion (split_if
) is part of the default setup.BTW, for each datatype
t
, the datatype package provides the split rulest.split
andt.split_asm
which provide case analysis forcase
expressions involving the typet
.Congruence rules
As already noted in the other answers, the
if_weak_cong
congruence rule prevents the simplifier from simplifying the branches of the if statement. In this answer, I want to elaborate a bit on the use of congruence rules by the simplifier.For further information, see also the chapter about the Simplifier in the Isabelle/Isar Reference Manual (in particular section 9.3.2).
Congruence rules control how the simplifier descends into terms. They can be used to limit rewriting and to provide additional assumptions. By default, if the simplifier encounters a function application
s t
it will descend both intos
andt
to rewrite them tos'
andt'
, before trying to rewrite the resulting terms' t'
.For each constant (or variable) c one can register a single congruence rule. The rule
if_weak_cong
is registered by default the constantIf
(which is underlying theif ... then ... else ...
syntax):This reads as: "If you encounter a term
if ?b then ?x else ?y
and?b
can be simplified to?c
, then rewriteif ?b then ?x else ?y
toif ?c then ?x else ?y
". As congruence rules replace the default strategy, this forbids any rewriting of?x
and?y
.An alternative to
if_weak_cong
is the strong congruence ruleif_cong
:Note the two assumptions
(?c ⟹ ?x = ?u)
and(¬ ?c ⟹ ?y = ?v)
: They tell the simplifier that it may assume that the condition holds (or holds not) when simplifying the left (or right) branch of the if.As an example, consider the behaviour of the simplifier on the goal
and assume that we know nothing about
foo
. Then,apply simp
: with the ruleif_weak_cong
, this will be simplified toif foo then ¬ foo ∨ False else foo ⟹ False
, only the condition is rewrittenapply (simp cong del: if_weak_cong)
: Without any congruence rule, this will be simplified toif foo then ¬ foo else foo ⟹ False
, as the condition and the branches are rewrittenapply (simp cong: if_cong del: if_cancel)
: With the ruleif_cong
, this goal will simplified toif foo then False else False ⟹ False
: The conditionfoo ∨ False
will be rewritten tofoo
. For the two branches, the simplifier now rewritesfoo ⟹ ¬foo ∨ False
and¬foo ⟹ foo ∨ False
, both of which obviously rewrite to False.(I removed
if_cancel
, which would usually solve the remaining goal completely)Another natural way of making progress in a proof containing
if _ then _ else _
is case analysis on the condition, e.g.,or if
foo
is not a free variable, but bound by an outermost meta-level universal quantifier (as is often the case in apply-scripts):Unfortunately, if
foo
is bound by another kind of quantifier, e.g.or by a meta-level universal quantifier in a nested assumption, e.g.
neither
cases
norcase_tac
are applicable.Note: See also here for the (slight) difference between
cases
andcase_tac
.