Zulip Chat Archive

Stream: new members

Topic: term mode partial function correctness proof?


Johannes C. Mayer (Nov 22 2021 at 18:00):

I am trying to do the exercise at the end of 7.2 in theorem proving in LEAN. I am a bit confused. How can you prove the second example in term mode?

namespace hidden
universe u

variables {α β γ : Type*}

inductive option (α : Type u) : Type u
| none : option
| some : α  option

def comp_partial (f : α  option β) (g : β  option γ) : α  option γ :=
λ ha, option.cases_on (f ha) option.none (λ hb, g hb)

variables (f : α  option β)
          (g : β  option γ)

example (x : α) (h₁ : f x = option.none) :
  ((comp_partial f g) x) = option.none :=
begin
  unfold comp_partial,
  rewrite h₁,
end

-- How to do this in term mode?
example (x : α) (h₁ : f x = option.none) :
  ((comp_partial f g) x) = option.none :=
sorry

end hidden

Mario Carneiro (Nov 22 2021 at 18:04):

the short answer is that you shouldn't, tactic mode does this one better

Mario Carneiro (Nov 22 2021 at 18:08):

The long answer is

example (x : α) (h₁ : f x = option.none) : ((comp_partial f g) x) = option.none :=
@congr_arg _ (option γ) _ _ (λ y, option.cases_on y option.none g) h₁

Patrick Johnson (Nov 22 2021 at 18:11):

Use show_term to see what the tactics compiled into:

begin
  show_term {
    unfold comp_partial,
    rewrite h₁}
end

Johannes C. Mayer (Nov 22 2021 at 18:14):

@Patrick Johnson That does give me something, but it is extremely complicated, or at least pretty long. Right now I also don't get Mario's answer but that seems much easier to understand.

Mario Carneiro (Nov 22 2021 at 18:15):

The hard part in the proof is rw, which makes for messy term proofs. I will usually use rw even in a term mode proof because it's more reliable and about as fast

Mario Carneiro (Nov 22 2021 at 18:16):

The basic idea is that if you have some goal P x containing a subterm x and you want to replace it with y given an equation h : x = y, then use congr_arg P h which has the type P x = P y

Mario Carneiro (Nov 22 2021 at 18:16):

the hard part is that you have to write down P, where rw will figure it out automatically by finding all instances of x in the goal and replacing them with a lambda variable

Mario Carneiro (Nov 22 2021 at 18:18):

the other hard part in this particular problem is that option.cases_on has a dependent type and this gives the elaborator trouble once it is removed from the context of comp_partial where the type is known. This is why I had to specify the type option γ to congr_arg

Mario Carneiro (Nov 22 2021 at 18:20):

The term used by rw involves eq.rec, which is very difficult to write manually unless you specify a bunch of implicit arguments because it has some unusual elaboration rules

Johannes C. Mayer (Nov 22 2021 at 18:24):

It also seems like even show_term can't handle eq.rec. I get: "invalid 'eq.rec' application, elaborator has special support for this kind of application (it is handled as an "eliminator"), but expected type must not contain metavariables", when I simply click on the 'try this' of show_term.

Mario Carneiro (Nov 22 2021 at 18:29):

that's what I mean

Mario Carneiro (Nov 22 2021 at 18:30):

the lean 3 pretty printer has trouble round-tripping at the default printing settings when eq.rec is involved

Mario Carneiro (Nov 22 2021 at 18:31):

you have to set_option pp.all true or so, which will also make the output rather hard to read

Johannes C. Mayer (Nov 22 2021 at 18:41):

YES, that works. And also it makes the proof just hilariously long :joy: . 101 lines in fact.

Johannes C. Mayer (Nov 22 2021 at 19:13):

@Mario Carneiro I do get your proof now I think. It creates the term option.cases_on (f x) option.none g = option.none right? And then LEAN looks at this and notices that it is definitionally equal to comp_partial f g x = option.none.

Is there some way to make this last step more explicit? E.g. could we specify a rewrite operation to rewrite option.cases_on (f x) option.none g to comp_partial f g x?


Last updated: Dec 20 2023 at 11:08 UTC