fix: discrepancy theorem vs example (#4493)

When the type of an `example` is a proposition,
we should elaborate on them as we elaborate on theorems.
This is particularly important for examples that are often
used in educational material.

Recall that when elaborating theorem headers, we convert unassigned
universe metavariables into universe parameters. The motivation is
that the proof of a theorem should not influence its statement.
However, before this commit, this was not the case for examples when
their type was a proposition.
This discrepancy often confused users.

Additionally, we considered extending the above behavior to definitions
when
1- When their type is a proposition. However, it still caused disruption
in Mathlib.
2- When their type is provided. That is, we would keep the current
behavior only if `: <type>` was omitted. This would make the elaborator
for `def` much closer to the one for `theorem`, but it proved to be too
restrictive.
For example, the following instance in `Core.lean` would fail:
```
instance {α : Sort u} [Setoid α] : HasEquiv α :=
  ⟨Setoid.r⟩
```
and we would have to write instead:
```
instance {α : Sort u} [Setoid α] : HasEquiv.{u, 0} α :=
  ⟨Setoid.r⟩
```
There are other failures like this in the core, and we assume many more
in Mathlib.

closes #4398
closes #4482 Remark: PR #4482 implements option 1 above. We may consider
it again in the future.
This commit is contained in:
Leonardo de Moura 2024-06-24 03:18:41 +02:00 committed by GitHub
parent 0f416c6a83
commit e3578c2f36
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 39 additions and 27 deletions

View file

@ -846,7 +846,10 @@ private def levelMVarToParamHeaders (views : Array DefView) (headers : Array Def
let rec process : StateRefT Nat TermElabM (Array DefViewElabHeader) := do
let mut newHeaders := #[]
for view in views, header in headers do
if view.kind.isTheorem then
-- Remark: we should consider using `pure view.kind.isTheorem <||> isProp header.type`, and
-- also handle definitions. We used the following approach because it is less disruptive to Mathlib.
-- Moreover, the type of most definitions are not propositions anyway.
if ← pure view.kind.isTheorem <||> (pure view.kind.isExample <&&> isProp header.type) then
newHeaders ←
withLevelNames header.levelNames do
return newHeaders.push { header with type := (← levelMVarToParam header.type), levelNames := (← getLevelNames) }

View file

@ -1,21 +1,21 @@
1682.lean:1:25-2:17: error: unsolved goals
case a
p : Sort ?u
p : Sort u_1
q r : Prop
⊢ p → q
case b
p : Sort ?u
p : Sort u_1
q r : Prop
⊢ r
1682.lean:4:25-5:26: error: unsolved goals
case a
p : Sort ?u
p : Sort u_1
q r : Prop
h : p
⊢ q
case b
p : Sort ?u
p : Sort u_1
q r : Prop
⊢ r

View file

@ -1,12 +1,12 @@
p : Nat → Prop
h : p 1
⊢ p 1
α : Sort ?u
α : Sort u_1
a : α
p : α → Prop
h : p a
⊢ p a
α : Sort ?u
α : Sort u_1
a : α
f : αα
p : α → Prop

View file

@ -82,9 +82,9 @@
{"textDocument": {"uri": "file:///plainGoal.lean"},
"position": {"line": 55, "character": 3}}
{"rendered":
"```lean\nα : Sort ?u\np : α → Prop\na b : α\ninst✝ : DecidablePred p\nh : ∀ {p : α → Prop} [inst : DecidablePred p], p a → p b\n⊢ p a\n```",
"```lean\nα : Sort u_1\np : α → Prop\na b : α\ninst✝ : DecidablePred p\nh : ∀ {p : α → Prop} [inst : DecidablePred p], p a → p b\n⊢ p a\n```",
"goals":
["α : Sort ?u\np : α → Prop\na b : α\ninst✝ : DecidablePred p\nh : ∀ {p : α → Prop} [inst : DecidablePred p], p a → p b\n⊢ p a"]}
["α : Sort u_1\np : α → Prop\na b : α\ninst✝ : DecidablePred p\nh : ∀ {p : α → Prop} [inst : DecidablePred p], p a → p b\n⊢ p a"]}
{"textDocument": {"uri": "file:///plainGoal.lean"},
"position": {"line": 61, "character": 3}}
{"rendered": "```lean\ncase left\n⊢ True\n```", "goals": ["case left\n⊢ True"]}
@ -110,15 +110,15 @@
{"textDocument": {"uri": "file:///plainGoal.lean"},
"position": {"line": 82, "character": 53}}
{"rendered":
"```lean\ncase nil\nα : Type ?u\nbs cs : List α\n⊢ [] ++ bs ++ cs = [] ++ (bs ++ cs)\n```",
"```lean\ncase nil\nα : Type u_1\nbs cs : List α\n⊢ [] ++ bs ++ cs = [] ++ (bs ++ cs)\n```",
"goals":
["case nil\nα : Type ?u\nbs cs : List α\n⊢ [] ++ bs ++ cs = [] ++ (bs ++ cs)"]}
["case nil\nα : Type u_1\nbs cs : List α\n⊢ [] ++ bs ++ cs = [] ++ (bs ++ cs)"]}
{"textDocument": {"uri": "file:///plainGoal.lean"},
"position": {"line": 82, "character": 54}}
{"rendered":
"```lean\ncase nil\nα : Type ?u\nbs cs : List α\n⊢ [] ++ bs ++ cs = [] ++ (bs ++ cs)\n```",
"```lean\ncase nil\nα : Type u_1\nbs cs : List α\n⊢ [] ++ bs ++ cs = [] ++ (bs ++ cs)\n```",
"goals":
["case nil\nα : Type ?u\nbs cs : List α\n⊢ [] ++ bs ++ cs = [] ++ (bs ++ cs)"]}
["case nil\nα : Type u_1\nbs cs : List α\n⊢ [] ++ bs ++ cs = [] ++ (bs ++ cs)"]}
{"textDocument": {"uri": "file:///plainGoal.lean"},
"position": {"line": 86, "character": 38}}
{"rendered": "no goals", "goals": []}

View file

@ -13,7 +13,7 @@ a : α
h : α = β
⊢ ⋯ ▸ a = b
ppProofs.lean:10:50-10:98: error: unsolved goals
α β : Sort ?u
α β : Sort u_1
b : β
a : α
h : α = β

View file

@ -1,2 +1,6 @@
example [Subsingleton α] (p : α → Prop) : Subsingleton (Subtype p) :=
⟨fun ⟨x, _⟩ ⟨y, _⟩ => Subsingleton.elim x y ▸ sorry⟩
example [Subsingleton α] (p : α → Prop) : Subsingleton (Subtype p) := by
constructor
intro ⟨x, _⟩ ⟨y, _⟩
have := Subsingleton.elim x y
subst this
rfl

5
tests/lean/run/4398.lean Normal file
View file

@ -0,0 +1,5 @@
example {ι : Type _} (n m : ι) (h : n = m) : m = n := by
simp only [id h] -- should work
example {ι : Type _} (n m : ι) (h : n = m) : m = n := by
simp only [h] -- works

View file

@ -24,7 +24,7 @@ def Vec.casesOn
| ⟨as, h⟩ => go n as h
/--
info: α : Type _
info: α : Type u_1
n✝ : Nat
a✝ : α
as✝ : Vec α n✝
@ -43,7 +43,7 @@ example (n : Nat) (a : α) (as : Vec α n) : Vec.cons a (Vec.cons a as) = Vec.co
constructor
/--
info: α : Type _
info: α : Type u_1
n✝ : Nat
a✝ : α
as✝ : Vec α n✝
@ -62,7 +62,7 @@ example (n : Nat) (a : α) (as : Vec α n) : Vec.cons a (Vec.cons a as) = Vec.co
constructor
/--
info: α : Type _
info: α : Type u_1
n : Nat
a : α
as : Vec α n

View file

@ -1,41 +1,41 @@
α : Type ?u
α : Type u_1
a : α
as bs : List α
h : bs = a :: as
⊢ (?head :: as).length = bs.length
case head
α : Type ?u
α : Type u_1
a : α
as bs : List α
h : bs = a :: as
α
α : Type ?u
α : Type u_1
b a : α
as bs : List α
h : as = bs
⊢ as.length + 1 + 1 = bs.length + 2
α : Type ?u
α : Type u_1
b a : α
as bs : List α
h : as = bs
⊢ as.length + 1 + 1 = (b :: bs).length + 1
α : Type ?u
α : Type u_1
b a : α
as bs : List α
h : as = bs
⊢ as.length + 1 + 1 = bs.length + 1 + 1
α : Type ?u
α : Type u_1
b a : α
as bs : List α
h : as = bs
⊢ id (a :: b :: as).length = (b :: bs).length + 1
α : Type ?u
α : Type u_1
b a : α
as bs : List α
h : as = bs
⊢ (a :: b :: as).length = (b :: bs).length + 1
α : Type ?u
α : Type u_1
b a : α
as bs : List α
h : as = bs