Zulip Chat Archive
Stream: lean4
Topic: Pretty printing `let (a,b) := `
Tomas Skrivan (Mar 27 2023 at 20:15):
Is there a way to pretty print let (a,b) := ...
? It gets de-sugared into a match statement which are a bit hard to read when the expression gets a bit more complicated:
#check
let (a, b) := (0,1)
let (a, b) := (b, a + b)
let (a, b) := (b, a + b)
b
The output is:
match (0, 1) with
| (a, b) =>
match (b, a + b) with
| (a, b) =>
match (b, a + b) with
| (a, b) => b : ℕ
Tomas Skrivan (Mar 27 2023 at 20:57):
Here is my attempt, but it does not work
set_option pp.all true in
def bar :
(let (a, b) := (0,1)
let (a, b) := (b, a + b)
b) = 1 :=
by
rfl
@[app_unexpander bar.match_1] def unexpandMatch : Lean.PrettyPrinter.Unexpander
| `($(_) $motive:term $xy:term fun ($a:ident $b:ident : $A:term) => $val:term) => `(let ($a,$b) := $xy; $val)
| _ => throw ()
#check bar
James Gallicchio (Mar 29 2023 at 06:11):
Not only does it print ugly, it proves ugly too! I don't know how let binding hypotheses work, but it would be great if single case match expressions were treated specially so that e.g. let (a,b) := (0,1)
would introduce (a,b) = (0,1)
to the context
Mario Carneiro (Mar 29 2023 at 06:31):
that's not a thing though
Mario Carneiro (Mar 29 2023 at 06:32):
or rather, if you want an equality then that's not what let
does
Mario Carneiro (Mar 29 2023 at 06:33):
The set
tactic will introduce an equality hypothesis like this
Mario Carneiro (Mar 29 2023 at 06:33):
you can also use (r)cases
for this
James Gallicchio (Mar 29 2023 at 06:35):
I don't want to de-rail Tomas's question too much, but here is an example where I would want better pretty-printing and better proofing:
import Std
def div_mod (n d : Nat) (hd : d > 0) : Nat × Nat :=
if h:n < d then (0,n) else
have : n - d < n := by
apply Nat.sub_lt ?_ hd
apply Nat.lt_of_lt_of_le hd
apply Nat.ge_of_not_lt h
let res := div_mod (n-d) d hd
(res.1+1,res.2)
termination_by _ n _ _ => n
theorem Nat.div_mod_eq (n d : Nat) {hd}
: div_mod n d hd = (q,r) → n = q * d + r
:= by
intro h
induction n using Nat.strongInductionOn generalizing q r
case ind n ih =>
unfold div_mod at h
split at h
next hn =>
rcases h
simp
next hn =>
have : n - d < n := by
apply Nat.sub_lt ?_ hd
apply Nat.lt_of_lt_of_le hd
apply Nat.ge_of_not_lt hn
generalize hres : div_mod (n-d) d hd = res at h
have := ih (n-d) this hres
simp at h
rcases h with ⟨rfl,rfl⟩
rw [Nat.succ_mul,
Nat.add_comm _ d,
Nat.add_assoc d,
←this,
Nat.add_comm,
Nat.sub_add_cancel]
apply Nat.ge_of_not_lt hn
In particular, I'd want to write let (q,r) := div_mod (n-d) d hd
in the function instead of having to assign the product to an intermediate value.
James Gallicchio (Mar 29 2023 at 06:36):
oh wait this is a bad example because the proof still works with that :joy:
James Gallicchio (Mar 29 2023 at 06:36):
you can still see the finagling I have to do with generalize
Mario Carneiro (Mar 29 2023 at 06:41):
an alternative to the generalize
and the three lines after it is
revert h
rcases hres : div_mod (n-d) d hd with ⟨q, r⟩
simp; rintro rfl rfl
have := ih (n-d) this hres
Mario Carneiro (Mar 29 2023 at 06:43):
alternatively, you don't even need the cases in this example since
cases h
have := ih (n-d) this rfl
works
James Gallicchio (Mar 29 2023 at 06:46):
Yeah, I think the most readable way to do this is
match hres : div_mod (n-d) d hd with
| (q',r') =>
rw [hres] at h
cases h
have := ih (n-d) this hres
but that is still quite a bit of finagling for something that (IMO) should just work
Sebastian Ullrich (Mar 29 2023 at 08:20):
You can also just use another split at h
, it pretty much does what you asked for
James Gallicchio said:
let (a,b) := (0,1)
would introduce(a,b) = (0,1)
to the context
Sebastian Ullrich (Mar 29 2023 at 08:21):
That destructuring let
requires some sort of case split in the proof unless it is computed away seems quite reasonable to me
James Gallicchio (Mar 29 2023 at 08:31):
ah, you're totally right. match in hypothesis ==> split.
that might be an argument against delaborating it to a destructuring let.
James Gallicchio (Mar 29 2023 at 08:37):
though... on the other hand, perhaps it should be specially delaborated, and there should be a new tactic to perform split at <hyp>; case _ <vars> _ =>
, since I imagine that is often what you want to do.
James Gallicchio (Mar 29 2023 at 08:38):
(where the new var names are copied from the vars bound in the destructuring let)
Sebastian Ullrich (Mar 29 2023 at 08:38):
Well that would be unhygienic
James Gallicchio (Mar 29 2023 at 08:43):
fair enough. I guess if I had to explain it to a first-year I'd tell them that destructuring lets are just doing a match, and that's why split
works on both matches and let
s.....
Last updated: Dec 20 2023 at 11:08 UTC