Will unfold def. fold def.
ever achieve anything in a Coq proof?
Put differently: will there ever be a difference between these two sequences of applications of tactics?:
unfold def. fold def. cbn.
cbn.
Will unfold def. fold def.
ever achieve anything in a Coq proof?
Put differently: will there ever be a difference between these two sequences of applications of tactics?:
unfold def. fold def. cbn.
cbn.
If you have a look at the documentation for both fold
(https://coq.inria.fr/refman/proof-engine/tactics.html#coq:tacn.fold) and unfold
(https://coq.inria.fr/refman/proof-engine/tactics.html#coq:tacn.unfold) you can see that they do not expect the same kind of argument.
While unfold
takes an identifier as argument, fold
takes a term.
So if you have def x y
in your goal, you can unfold def
to access its definition, but then you might have to use fold (def x y)
to return to the original goal.
In any case, there is no guarantee that unfold def ; fold (def x y)
will result in nothing, since there might other occurrences of the unfolded def x y
in the goal.
Here is a concrete example to see fold
and unfold
in action.
If the goal changes after a tactic, I put the new goal in a comment after the tactic. Note also the use of Fail progress tac
which asserts that executing tactic tac
will not affect the goal at all.
Definition foo (b : bool) :=
if b then 0 else 1.
Goal foo true + 1 = foo false.
Proof.
unfold foo.
(* 0 + 1 = 1 *)
Fail progress fold foo.
fold (foo true).
(* foo true + S (foo true) = S (foo true) *)
Fail progress fold (foo false).
unfold foo.
(* 0 + 1 = 1 *)
fold (foo false).
(* 0 + foo false = foo false *)
fold (foo true).
(* foo true + foo false = foo false *)
unfold foo at 2.
(* foo true + 1 = foo false *)
As you can see, fold foo
will not do anything, while fold true
and fold false
will, of course, it's also greedy, any 0
will turn into fold true
.
Definitely. unfold def
inlines the definition of def
, and also performs some basic reductions e.g. if def
was applied to anything before it got inlined.
Definition hold {T} (x : T) : Prop := True.
Goal (not False -> hold not).
unfold not.
(* inline not: (fun A : Prop => A -> False) False -> hold (fun A : Prop => A -> False)
and reduce: (False -> False) -> hold (fun A : Prop => A -> False) *)
However, fold def
does not "un-reduce" applications of def
. If you now do
fold not.
it will not fold False -> False
back to not False
. It will only find the not
under the hold
, so you get (False -> False) -> hold not
as your goal. (fold (not False)
would reduce not False
to False -> False
, search for that, and then eventually give the goal not False -> hold (fun A : Prop => A -> False
, but, again, it hasn't properly undone the unfold
). So, basically, the sequence unfold def. fold def.
inlines and simplifies usages of def
where it has been "used" (e.g. applied), and tries to leave the other usages intact.
Another example, this time with iota-reduction (reducing a destruction) instead of beta-reduction.
Definition def : bool := true.
Definition truth (b : bool) : Prop := if b then True else False.
Goal ((if def then True else False) -> truth def).
unfold def.
(* inline def: (if true then True else False) -> truth true
and reduce: True -> truth true *)
fold def.
(* goal: True -> truth def *)
In the first case, cbn.
and unfold not. fold not. cbn.
are different (and in neither case does cbn
do anything). In this latter case, cbn.
just takes us straight to True -> True
no matter where we put it.