-
Notifications
You must be signed in to change notification settings - Fork 234
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
162 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/- | ||
Copyright (c) 2023 Mac Malone. All rights reserved. | ||
Released under Apache 2.0 license as described in the file LICENSE. | ||
Authors: Mac Malone, Kyle Miller | ||
-/ | ||
import Lean.Elab.MutualDef | ||
import Std.Tactic.OpenPrivate | ||
|
||
/-! | ||
# `recall` command | ||
-/ | ||
|
||
namespace Mathlib.Tactic.Recall | ||
|
||
/-- | ||
The `recall` command redeclares a previous definition for illustrative purposes. | ||
This can be useful for files that give an expository account of some theory in Lean. | ||
The syntax of the command mirrors `def`, so all the usual bells and whistles work. | ||
``` | ||
recall List.cons_append (a : Ξ±) (as bs : List Ξ±) : (a :: as) ++ bs = a :: (as ++ bs) := rfl | ||
``` | ||
Also, one can leave out the body. | ||
``` | ||
recall Nat.add_comm (n m : Nat) : n + m = m + n | ||
``` | ||
The command verifies that the new definition type-checks and that the type and value | ||
provided are definitionally equal to the original declaration. However, this does not | ||
capture some details (like binders), so the following works without error. | ||
``` | ||
recall Nat.add_comm {n m : Nat} : n + m = m + n | ||
``` | ||
-/ | ||
syntax (name := recall) "recall " ident ppIndent(optDeclSig) (declVal)? : command | ||
|
||
open Lean Meta Elab Command Term | ||
open private elabHeaders from Lean.Elab.MutualDef | ||
|
||
elab_rules : command | ||
| `(recall $id $sig:optDeclSig $[$val?]?) => withoutModifyingEnv do | ||
let some info := (β getEnv).find? id.getId | ||
| throwError "unknown constant '{id}'" | ||
runTermElabM fun _ => discard <| addTermInfo id (mkConst id.getId) | ||
let id' := mkIdentFrom id (β mkAuxName id.getId 1) | ||
if let some val := val? then | ||
let some infoVal := info.value? | ||
| throwErrorAt val "constant '{id}' has no defined value" | ||
elabCommand <| β `(noncomputable def $id':declId $sig:optDeclSig $val) | ||
let some newInfo := (β getEnv).find? id'.getId | return -- def already threw | ||
runTermElabM fun _ => do | ||
let mvs β newInfo.levelParams.mapM fun _ => mkFreshLevelMVar | ||
let newType := newInfo.type.instantiateLevelParams newInfo.levelParams mvs | ||
unless (β isDefEq info.type newType) do | ||
throwTypeMismatchError none info.type newInfo.type (mkConst id.getId) | ||
let newVal := newInfo.value?.get!.instantiateLevelParams newInfo.levelParams mvs | ||
unless (β isDefEq infoVal newVal) do | ||
let err := | ||
m!"value mismatch{indentD id.getId}\nhas value{indentExpr newVal}\n" ++ | ||
m!"but is expected to have value{indentExpr infoVal}" | ||
throwErrorAt val err | ||
else | ||
let (binders, type?) := expandOptDeclSig sig | ||
let views := #[{ | ||
declId := id', binders, type?, value := .missing, | ||
ref := β getRef, kind := default, modifiers := {} | ||
: DefView}] | ||
runTermElabM fun _ => do | ||
let elabView := (β elabHeaders views)[0]! | ||
unless (β isDefEq info.type elabView.type) do | ||
throwTypeMismatchError none info.type elabView.type (mkConst id.getId) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import Std.Tactic.GuardMsgs | ||
import Mathlib.Tactic.Recall | ||
import Mathlib.Analysis.Calculus.Deriv.Basic | ||
import Mathlib.Analysis.SpecialFunctions.Trigonometric.Basic | ||
import Mathlib.Data.Complex.Exponential | ||
|
||
-- Remark: When the test is run by make/CI, this option is not set, so we set it here. | ||
set_option pp.unicode.fun true | ||
|
||
/- | ||
Motivating examples from the initial Zulip thread: | ||
https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/recall.20command | ||
-/ | ||
|
||
section | ||
variable {π : Type _} [NontriviallyNormedField π] | ||
variable {E : Type _} [NormedAddCommGroup E] [NormedSpace π E] | ||
variable {F : Type _} [NormedAddCommGroup F] [NormedSpace π F] | ||
recall HasFDerivAtFilter (f : E β F) (f' : E βL[π] F) (x : E) (L : Filter E) := | ||
(fun x' => f x' - f x - f' (x' - x)) =o[L] fun x' => x' - x | ||
end | ||
|
||
/-- | ||
error: value mismatch | ||
Complex.exp | ||
has value | ||
id | ||
but is expected to have value | ||
fun z β¦ CauSeq.lim (Complex.exp' z) | ||
-/ | ||
#guard_msgs in recall Complex.exp : β β β := id | ||
|
||
/-- | ||
error: value mismatch | ||
Real.pi | ||
has value | ||
0 | ||
but is expected to have value | ||
2 * Classical.choose Real.exists_cos_eq_zero | ||
-/ | ||
#guard_msgs in recall Real.pi : β := 0 | ||
|
||
/- | ||
Other example tests | ||
-/ | ||
|
||
recall id (x : Ξ±) : Ξ± := x | ||
|
||
/-- | ||
error: type mismatch | ||
id | ||
has type | ||
{Ξ± : Sort u_1} β Ξ± β Ξ± β β : Type u_1 | ||
but is expected to have type | ||
{Ξ± : Sort u} β Ξ± β Ξ± : Sort (imax (u + 1) u) | ||
-/ | ||
#guard_msgs in recall id (_x _y : Ξ±) : β := 0 | ||
|
||
recall id (_x : Ξ±) : Ξ± | ||
|
||
def foo := 22 | ||
|
||
recall foo := 22 | ||
|
||
recall foo : Nat | ||
|
||
/-- | ||
error: value mismatch | ||
foo | ||
has value | ||
23 | ||
but is expected to have value | ||
22 | ||
-/ | ||
#guard_msgs in recall foo := 23 | ||
|
||
recall Nat.add_comm (n m : Nat) : n + m = m + n | ||
|
||
-- Caveat: the binder kinds are not checked | ||
recall Nat.add_comm {n m : Nat} : n + m = m + n | ||
|
||
/-- error: unknown constant 'nonexistent' -/ | ||
#guard_msgs in recall nonexistent | ||
|
||
axiom bar : Nat | ||
/-- error: constant 'bar' has no defined value -/ | ||
#guard_msgs in recall bar := bar | ||
|
||
recall List.cons_append (a : Ξ±) (as bs : List Ξ±) : (a :: as) ++ bs = a :: (as ++ bs) := rfl |