Zulip Chat Archive

Stream: new members

Topic: theorem foo (t : T) vs. theorem foo {t : T}


Isak Colboubrani (Feb 26 2024 at 22:29):

When are the following two theorems not equivalent?

theorem foo₁ (a b c : G) (h : a * b = a * c) : b = c := by sorry
theorem foo₂ {a b c : G} (h : a * b = a * c) : b = c := by sorry

Of the theorems in Mathlib it seems like roughly 57 % are on the form theorem … (… : T) … and 43 % are on the form theorem … {… : T} ….

Kevin Buzzard (Feb 26 2024 at 22:51):

They're equivalent in the sense that they have the same mathematical content. They're not equivalent in the sense that foo1 is more irritating to use because h is an input and once Lean sees h it can figure out a, b, c, but foo1 wastes the user's time asking for them anyway.

Damiano Testa (Feb 26 2024 at 23:18):

And you can also verify what Kevin said about the lemmas being equivalent:

variable {G} [Mul G]
theorem foo₁ (a b c : G) (h : a * b = a * c) : b = c := sorry
theorem foo₂ {a b c : G} (h : a * b = a * c) : b = c := sorry

example : @foo₁ = @foo₂ := rfl

Isak Colboubrani (Feb 26 2024 at 23:37):

Thanks! I think I follow.

I'm trying to formulate a rule on when to use one over the other.

In the input case (h above): then it sounds like foo₂ {a b c : G} is always preferable over foo₁ (a b c : G)?

In the non-input case such as …

theorem bar₁ (a b : G) : (a * b)⁻¹ = b⁻¹ * a⁻¹ := by sorry
theorem bar₂ {a b : G} : (a * b)⁻¹ = b⁻¹ * a⁻¹ := by sorry

… then there is no ergonomic difference between the two?

The reason I'm asking is that I saw the different forms being mixed in files such as https://github.com/leanprover-community/lftcm2020/blob/master/src/exercises_sources/wednesday/algebraic_hierarchy.lean where we have:

lemma mul_left_cancel (a b c : G) (Habac : a * b = a * c) : b = c := 
lemma mul_eq_of_eq_inv_mul {a x y : G} (h : x = a⁻¹ * y) : a * x = y := 
theorem mul_one (a : G) : a * 1 = a := 

Damiano Testa (Feb 26 2024 at 23:43):

My point of view is that your initial examples foo_i had two "defensible" choices: either all of a b c should be implicit, or a should be explicit and b c implicit. The reason for possibly wanting a to be explicit is that, if you do not have h in context, by passing foo_i a Lean will produce the correct side-goal.

This is a subtlety, though, and it is not clear that the annoyance of using foo_i _ beats the few cases where having the explicit argument is useful.

Kyle Miller (Feb 27 2024 at 02:26):

You can write foo₂ (a := val) if you want to set a, though this isn't that great since the name a isn't necessarily intended to be part of the interface.


Last updated: May 02 2025 at 03:31 UTC