Zulip Chat Archive
Stream: new members
Topic: rewriting in dependent expressions
Scott Buckley (Jan 09 2026 at 09:27):
import Mathlib.Data.Vector.Basic
lemma foo (fnA : (n m k : Nat) → (n = m + k) → Vector Nat n)
(fnB : (n : Nat) → Vector Nat n) (a b : Nat):
fnA (a + (b + 1)) a (b + 1) (by omega) = fnB (a + (b + 1)) →
fnA (a.succ + b) a (b + 1) (by omega) = fnB (a.succ + b) := by
intros h1
have h2 : (a + (b + 1)) = (a.succ + b) := by omega
-- here I am unable to do any rewriting with h2, nor can I generalize
-- the relevant expressions.
grind -- but grind works
In the above MWE, I have an equality where the type of the expressions are dependent on some of the subterms. There are also some other dependencies in the expression you can see. I have an equality that theoretically could let me rewrite some subterms, thus allowing me to use my assumption, but doing so non-universally would break the type-correctness of the expressions. grind will get the job done, but I have been unable to extract any useful information about how it's getting it done, and nevertheless I am more interested in how this might be solved without grind.
In such a circumstance, do you have any tips for how to proceed in this proof? Are there any clever dependent rewriting tricks? I could go viaHeq and cast I think, although I'm not sure this would necessarily help, as the LHS and RHS of this equality each would break with non-global rewrites.
A way I found to solve this problem was to write a similar lemma, where the complex terms in question were reduced to a single variable, which then allowed me to perform a "global rewrite" by performing case destruction on a given equality, as in the following example. It seems that I should be able to get to a similar state in the foo proof via generalizing, but generalizing also couldn't cope with the dependencies.
import Mathlib.Data.Vector.Basic
lemma foo_aux (fnA : (n m k : Nat) → (n = m + k) → Vector Nat n)
(fnB : (n : Nat) → Vector Nat n) (a b x y : Nat)
(inv1 : x = a + b) (inv2 : y = a + b):
x = y →
fnA x a b inv1 = fnB x →
fnA y a b inv2 = fnB y := by
intros hxy h
-- rw [← hxy] -- does not work
cases hxy
exact h
(side note: conv seems to skip over the dependent term a + (b + 1), presumably because it is used in a type dependency. It would be lovely to have a version of conv that allowed rewriting in dependent terms, but carried around the consequences of these rewrites to enforce that other subterms were appropriately rewritten. I suppose this would mean that the entire conv system would need to allow for types to change recursively, and I can see this being non-trivial. Further, propositions depending on these terms would potentially need to be re-proven, although if we are only replacing equal terms then perhaps this isn't a problem.)
Yan Yablonovskiy 🇺🇦 (Jan 09 2026 at 09:29):
If you are okay with Mathlib tactics you can do:
import Mathlib.Data.Vector.Basic
import Mathlib.Tactic
lemma foo_aux (fnA : (n m k : Nat) → (n = m + k) → Vector Nat n)
(fnB : (n : Nat) → Vector Nat n) (a b x y : Nat)
(inv1 : x = a + b) (inv2 : y = a + b):
x = y →
fnA x a b inv1 = fnB x →
fnA y a b inv2 = fnB y := by
intros hxy h
rw! [← hxy] -- works
cases hxy
exact h
which is like a 'dependent' rewrite
Scott Buckley (Jan 09 2026 at 09:34):
Beautiful! I did a bunch of googling about "dependent rewriting" for Lean, but never came across this. I have seen ! versions of a few tactics used here and there - is it a pattern that this refers to versions of the tactics supporting more dependency? I should read more thoroughly through the documentation for mathlib's tactics.
Thank you for the insanely fast response!
Ruben Van de Velde (Jan 09 2026 at 09:48):
"!" means "try harder" in some way - in contrapose! or by_contra! it means "push the negation inwards", for example
Scott Buckley (Jan 09 2026 at 10:54):
Yeah, this makes sense, thank you.
While we're on the topic, I'm finding that I'm unable to perform rw! [foo] at h in my original environment, although I haven't been able to recreate the issue in my MWE. It looks like a very similar to the below:
import Mathlib.Data.Vector.Basic
import Mathlib.Tactic
lemma foo_aux (fnA : (n m k : Nat) → (n = m + k) → Vector Nat n)
(fnB : (n : Nat) → Vector Nat n) (a b x y : Nat)
(inv1 : x = a + b) (inv2 : y = a + b):
x = y →
fnA x a b inv1 = fnB x →
fnA y a b inv2 = fnB y := by
intros hxy h
rw! [hxy] at h -- here is where the issue occurs
cases hxy
exact h
I get the following error, in a context where I can't see any reason the rewrite shouldn't work - it's almost identical to in the MWE.
unknown free variable `_fvar.18510`
Does this ring any bells already? I'm just checking because it might be a fair bit of work to get a MWE exhibiting the same behaviour.
Aaron Liu (Jan 09 2026 at 11:02):
Yes, I fixed that bug recently
Aaron Liu (Jan 09 2026 at 11:03):
as a workaround you can do rewrite! instead of rw!
Scott Buckley (Jan 09 2026 at 11:52):
@Aaron Liu that did the trick! Thanks :)
Aaron Liu (Jan 09 2026 at 19:35):
the fix was merged 2 days ago (#33686) so it should also be fine if you bump mathlib
Last updated: Feb 28 2026 at 14:05 UTC