Zulip Chat Archive

Stream: new members

Topic: Extracting left-hand side from an equality


Kevin Cheung (Jan 20 2024 at 13:42):

If I have something like h : a + b + c = 0, is there syntax to refer to the left-hand side a + b + c? I want to be able to do something like let g := (the left-side of the equality in h).

Kevin Buzzard (Jan 20 2024 at 13:44):

Can you un-xy? Why do you want to give the left hand side of the equality a name?

Kevin Cheung (Jan 20 2024 at 13:45):

Because I am going to refer to the left-hand side many times (not exactly this expression here) but don't want to retype the whole thing. (In my case, the left-hand side is very long.)

Matt Diamond (Jan 21 2024 at 02:44):

You know, there was a great trick for this in Lean 3, where you could do something like this:

-- Lean 3 code

import data.nat.basic

example (a b c d e : nat) (h : a + b + c + d + e = 0) : false :=
begin
  let foo := _,
  change foo = 0 at h,
  sorry,
end

Matt Diamond (Jan 21 2024 at 02:44):

I don't know if it works in Lean 4 though

Kyle Miller (Jan 21 2024 at 02:46):

Here's something we could have:

import Mathlib.Lean.Expr.Basic

open Lean Meta

elab "lhs%" t:term : term => do
  let e  Elab.Term.elabTerm t none
  if let some (_, lhs, _, _rhs) := ( inferType e).sides? then
    return lhs
  else
    throwError m!"Not an equality, iff, or HEq:{indentD e}"

elab "rhs%" t:term : term => do
  let e  Elab.Term.elabTerm t none
  if let some (_, _lhs, _, rhs) := ( inferType e).sides? then
    return rhs
  else
    throwError m!"Not an equality, iff, or HEq:{indentD e}"

example {a b c : Nat} (h : a + b + c = 0) : False := by
  let g := lhs% h
  /-
  g : Nat := a + b + c
  -/

Kyle Miller (Jan 21 2024 at 02:46):

@Matt Diamond I tried a few tricks, but couldn't get that to work.

Matt Diamond (Jan 21 2024 at 02:48):

That's unfortunate... it was pretty useful for handling unwieldy expressions

Matt Diamond (Jan 21 2024 at 02:50):

it would be cool if the change tactic automatically created variables for you if they don't already exist

Matt Diamond (Jan 21 2024 at 02:56):

@Kevin Cheung Are you familiar with the set tactic? You could use that to define a new variable and it should automatically update any hypotheses that match the definition. The only downside is that you'll need to write out the expression (but only once).

import Mathlib

example (a b c d e : ) (h : a + b * c - d + e = 0) : False := by
  set foo := a + b * c - d + e -- h's type is now `foo = 0`
  sorry

Jireh Loreaux (Jan 21 2024 at 04:16):

I think @Gareth Ma was working on a setm tactic for this a while back, with advice from @Thomas Murrills. There's probably a PR around somewhere.

Gareth Ma (Jan 21 2024 at 04:31):

Hi yes… my real life got busy and I couldn’t do any Lean the past weeks

Gareth Ma (Jan 21 2024 at 04:31):

I can find it, it’s somewhere

Gareth Ma (Jan 21 2024 at 04:32):

https://github.com/leanprover-community/mathlib4/pull/7890

Gareth Ma (Jan 21 2024 at 04:33):

I don’t remember exactly what’s missing, but it must be something I didn’t understand, otherwise I would’ve implemented it lol

Kyle Miller (Jan 21 2024 at 05:20):

@Matt Diamond I hacked your idea together

example {a b c : Nat} (h : a + b + c = 0) : False := by
  change x = _ at h
  /-
  a b c : ℕ
  x : ℕ := a + b + c
  h : x = 0
  ⊢ False
  -/

code

Kyle Miller (Jan 21 2024 at 05:22):

I'm not sure this is a good default for change, but it's interesting and worth considering as a tactic.

Matt Diamond (Jan 21 2024 at 05:29):

nice! yeah perhaps it could be a separate tactic

Matt Diamond (Jan 21 2024 at 05:31):

Gareth's setm seems similar in function, just with slightly different syntax

Yaël Dillies (Jan 21 2024 at 11:12):

I would have expected the syntax to be change ?x = _ at h

Eric Wieser (Jan 21 2024 at 13:10):

Or perhaps $x

Yaël Dillies (Jan 21 2024 at 13:25):

I think it shouldn't be a separate tactic if we manage to make one of Eric's or my syntax work

Kevin Cheung (Jan 21 2024 at 13:27):

Matt Diamond said:

Kevin Cheung Are you familiar with the set tactic? You could use that to define a new variable and it should automatically update any hypotheses that match the definition. The only downside is that you'll need to write out the expression (but only once).

import Mathlib

example (a b c d e : ) (h : a + b * c - d + e = 0) : False := by
  set foo := a + b * c - d + e -- h's type is now `foo = 0`
  sorry

I didn't know about set, only let and have. This could work. Thanks!

Kyle Miller (Jan 21 2024 at 19:09):

Yaël Dillies said:

I would have expected the syntax to be change ?x = _ at h

Sure -- my experiment was just to see if I could misuse the autoImplicits mechanism to get this to work. Maybe there will be a tactic one day where it'll be useful to auto-add additional let bindings like this.

Kyle Miller (Jan 21 2024 at 19:10):

Gareth's setm uses ?x and it seems like a better idea on the grounds that you can predict what the new let bindings will be.

Kyle Miller (Jan 22 2024 at 04:04):

I just discovered a hack to get the Lean 3 trick @Matt Diamond mentioned to work:

import Mathlib.Data.Nat.Basic

example (a b c d e : ) (h : a + b + c + d + e = 0) : False := by
  let foo : ?_ := ?_
  replace h := (by refine h : foo = 0)
  /-
  a b c d e : ℕ
  foo : ℕ := a + b + c + d + e
  h : foo = 0
  ⊢ False
  -/
  sorry

Kyle Miller (Jan 22 2024 at 04:04):

Here's a variation on it:

import Mathlib.Data.Nat.Basic

example (a b c d e : ) (h : a + b + c + d + e = 0) : False := by
  let foo : ?_ := ?_
  change type_of% (by refine h : foo = 0) at h
  /-
  a b c d e : ℕ
  foo : ℕ := a + b + c + d + e
  h : foo = 0
  ⊢ False
  -/
  sorry

Kyle Miller (Jan 22 2024 at 04:06):

There's a chance I just coded change ... at ... wrong and there's a way to get change foo = 0 at h by itself to work here. I think I remember that there was a tradeoff between change foo = 0 at h working and typeclass instance synthesis succeeding for things like numerals.

Kyle Miller (Jan 22 2024 at 06:51):

Getting change to work for this without breaking hasn't been successful yet. std4#555 is an attempt, but it seems to be choosing the wrong instances in Std.Data.Int.DivMod. I'm not sure why it's picking up on Nat instances rather than unifying with the Int ones if there's nothing that I can see triggering default instances.

Kevin Cheung (Jan 22 2024 at 11:44):

Kyle Miller said:

I just discovered a hack to get the Lean 3 trick Matt Diamond mentioned to work:

import Mathlib.Data.Nat.Basic

example (a b c d e : ) (h : a + b + c + d + e = 0) : False := by
  let foo : ?_ := ?_
  replace h := (by refine h : foo = 0)
  /-
  a b c d e : ℕ
  foo : ℕ := a + b + c + d + e
  h : foo = 0
  ⊢ False
  -/
  sorry

This is amazing. But it looks like magic to me. What are refine and replace actually doing under the hood?

Kyle Miller (Jan 22 2024 at 11:47):

The replace tactic is cheating and not actually changing h, but replacing it.

The key trick is that refine has the power to assign to ?_ metavariables at a certain step, and these are normally not assignable. (In std4#555 I tried giving change this power)

Kevin Cheung (Jan 22 2024 at 11:48):

I see.


Last updated: May 02 2025 at 03:31 UTC