Zulip Chat Archive

Stream: general

Topic: use vs. exists


Mirek Olšák (Dec 09 2025 at 10:45):

I was usually using exists, then someone mentioned to me that use is recommended. What is the difference? I thought I could see a difference in the examples provided by the docs of use but that is not much helpful, these are the examples in the docs of use:

example :  x : Nat, x = x := by use 42
-- all `use`, `exists`, `use!` succeed

example :  x : Nat,  y : Nat, x = y := by use 42, 42
-- all `use`, `exists`, `use!` succeed

example :  x : String × String, x.1 = x.2 := by use ("forty-two", "forty-two")
-- all `use`, `exists`, `use!` succeed

example :  p : Nat × Nat, p.1 = p.2 := by use! 42, 42
-- Error no matter which tactic I write, bug in the docs I suppose

example :  p : Nat × Nat, p.1 = p.2 := by use! (42, 42)
-- all `use`, `exists`, `use!` succeed

Mirek Olšák (Dec 09 2025 at 12:05):

Oh, I see in cases like

inductive Foo : Prop
| intro (x y : Nat) (h : x < y)

example (a b : Nat) (h : a < b) : Foo := by
  use a -- here exists fails

there should be also a more natural example without a custom structure...

Kyle Miller (Dec 09 2025 at 18:32):

The difference between exists and use doesn't really appear until you have constructors with more than two arguments.

The use! tactic is able to flatten structures. Unlike you, I'm seeing that use! succeeds in the following example (using the latest Mathlib on live.lean-lang.org)

example :  p : Nat × Nat, p.1 = p.2 := by use! 42, 42

You can do use! 42 as well.

Kyle Miller (Dec 09 2025 at 18:33):

Maybe the difference is that I tested it with import Mathlib.Tactic.Common. Importing all of Mathlib breaks it for some reason.

Kyle Miller (Dec 09 2025 at 18:34):

Ah, if you do use! 42 you can see the problem. It's using the OfNat instance for ℕ × ℕ. The use! tactic tries elaborating a value for a constructor field, and if that fails, then it tries going into that constructor field. Unfortunately for this example, elaboration succeeds for ℕ × ℕ.

Kyle Miller (Dec 09 2025 at 18:36):

Here's one that works with all of Mathlib imported:

example :  p : Bool × Nat, p.1 = p.2 := by use! true, 1; rfl

Mirek Olšák (Dec 09 2025 at 18:38):

Interesting, I am usually just doing import Mathlib, sometimes import Mathlib.Tactic

Kyle Miller (Dec 09 2025 at 18:40):

It's a surprise, but at least it's not a bug.

For what it's worth, this example is in MathlibTest/Use.lean, to make sure the docstring is accurate. (That said, the "forty-two" example is not in the test file...)

Mirek Olšák (Dec 09 2025 at 18:46):

Even if if it is not a bug, wouldn't it be helpful to have more stable & demonstrative examples?

Mirek Olšák (Dec 09 2025 at 18:50):

Or maybe use!just cannot be stable under different import...

Kyle Miller (Dec 09 2025 at 19:14):

Feel free to make a PR to improve the docstring, with some more realistic examples (and make sure the test file actually includes all the docstring examples). You can't completely protect against all possible imports, but you can at least protected against obvious culprits — in this case the cartesian product has a natural algebraic structure.

Kyle Miller (Dec 09 2025 at 19:14):

You could also use a type ascription in the use argument to protect against it, and use the example as a gotcha of use!.

Mirek Olšák (Dec 10 2025 at 00:34):

Ok, will think about better examples, I am not sure if I understand it perfectly but hopefully better now.

Mirek Olšák (Dec 10 2025 at 13:25):

Perhaps these examples?

  1. use, but not exists
example : Nonempty Nat := by use 5
example : Nonempty (+  ) := by
  use PNat.natPred, Nat.succPNat
  · exact PNat.succPNat_natPred
  · intro; rfl
  1. use! but not use
example :  n : {n : Nat // n%2 = 0}, n.val > 10 := by use! 20; simp

Kyle Miller (Dec 10 2025 at 17:22):

Those seem reasonable. I'm actually surprised that the second example can be done with use; I'd have expected use! since this needs to first apply the Nonempty constructor.

Mirek Olšák (Dec 10 2025 at 18:38):

Kyle Miller said:

Feel free to make a PR to improve the docstring, with some more realistic examples (and make sure the test file actually includes all the docstring examples).

Like this? https://github.com/leanprover-community/mathlib4/pull/32699
I needed to import some more files to make the example with PNat working, which also caused the notation in the tests.

Violeta Hernández (Dec 10 2025 at 23:08):

I've not once used exists. I always assumed it was some kind of holdover, or one of those "for teaching only" tactics.

Mirek Olšák (Dec 11 2025 at 11:33):

Violeta Hernández said:

I've not once used exists. I always assumed it was some kind of holdover, or one of those "for teaching only" tactics.

Poor exists... On the other hand, isn't it the case that exists is good enough for 90% cases of your applications of use? I would say exists is a natural word to write when you don't know the tactic but I would approve making it a direct synonym to use. Then I am also aware of existsi which doesn't try the automatic discharging (so its output is more predictable).

Michael Rothgang (Dec 11 2025 at 11:38):

To me, using use to fill an existential feels more natural thanexists.

Mirek Olšák (Dec 11 2025 at 11:47):

Nothing against that, perhaps I just knew exists from Rocq, or an older Lean version... but what is the long-term plan with exists & existsi when they seem to be almost forgotten by the community?

Mirek Olšák (Dec 11 2025 at 11:51):

I am actually a little surprised that existsi is a Mathlib tactic, and smarter exists is a Core tactic.

Kyle Miller (Dec 11 2025 at 16:25):

Maybe one day the core exists will do what use does, and mathlib's can be a synonym. That seems like a reasonable outcome. For the short term though it seems low-priority to do anything about it.

Something that would be neat is if use could avoid splitting the goal, if it would reassemble the goal into an existential as needed.

It would also be neat if use could make use of the names of constructor fields or of the binders for existentials. For example, if your goal was exists x, exists y, ..., imagine being able to do use y := 3. Or if the goal was a structure, write use invFun := fun x => .... Havig use reassemble the goal as an existential would enable you to use use to introduce choices one at a time.


Last updated: Dec 20 2025 at 21:32 UTC