Skip to content

Commit 60d5441

Browse files
committed
feat: port the 'alias' command (#293)
Compare to https://github.com/leanprover-community/mathlib/blob/07c83c81c0016c1ac08e8c9c3f1260c2f4c6ac7f/src/tactic/alias.lean The `mkIffMpApp` and `aliasIff` functions are direct translations of the mathlib3 versions.
1 parent c46f8b6 commit 60d5441

File tree

4 files changed

+230
-2
lines changed

4 files changed

+230
-2
lines changed

Mathlib.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import Mathlib.Mathport.Attributes
5757
import Mathlib.Mathport.Rename
5858
import Mathlib.Mathport.SpecialNames
5959
import Mathlib.Mathport.Syntax
60+
import Mathlib.Tactic.Alias
6061
import Mathlib.Tactic.Basic
6162
import Mathlib.Tactic.Cache
6263
import Mathlib.Tactic.Cases

Mathlib/Mathport/Syntax.lean

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Authors: Mario Carneiro
55
-/
66
import Lean.Elab.Command
77
import Lean.Elab.Quotation
8+
import Mathlib.Tactic.Alias
89
import Mathlib.Tactic.Cases
910
import Mathlib.Tactic.Core
1011
import Mathlib.Tactic.CommandQuote
@@ -582,8 +583,6 @@ namespace Command
582583
(" from" (ppSpace ident)+)? (" := " str)? : command
583584

584585
/- M -/ syntax (name := addHintTactic) "add_hint_tactic " tactic : command
585-
/- M -/ syntax (name := alias) "alias " ident " ← " ident* : command
586-
/- M -/ syntax (name := aliasLR) "alias " ident " ↔ " (".." <|> (binderIdent binderIdent)) : command
587586

588587
/- S -/ syntax (name := explode) "#explode " ident : command
589588

Mathlib/Tactic/Alias.lean

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/-
2+
Copyright (c) 2017 Mario Carneiro. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Mario Carneiro, David Renshaw
5+
-/
6+
import Lean.Elab.Command
7+
import Lean.Elab.Term
8+
import Lean
9+
10+
/-!
11+
# The `alias` command
12+
13+
This file defines an `alias` command, which can be used to create copies
14+
of a theorem or definition with different names.
15+
16+
Syntax:
17+
18+
```lean
19+
/-- doc string -/
20+
alias my_theorem ← alias1 alias2 ...
21+
```
22+
23+
This produces defs or theorems of the form:
24+
25+
```lean
26+
/-- doc string -/
27+
theorem alias1 : <type of my_theorem> := my_theorem
28+
29+
/-- doc string -/
30+
theorem alias2 : <type of my_theorem> := my_theorem
31+
```
32+
33+
Iff alias syntax:
34+
35+
```lean
36+
alias A_iff_B ↔ B_of_A A_of_B
37+
alias A_iff_B ↔ ..
38+
```
39+
40+
This gets an existing biconditional theorem `A_iff_B` and produces
41+
the one-way implications `B_of_A` and `A_of_B` (with no change in
42+
implicit arguments). A blank `_` can be used to avoid generating one direction.
43+
The `..` notation attempts to generate the 'of'-names automatically when the
44+
input theorem has the form `A_iff_B` or `A_iff_B_left` etc.
45+
-/
46+
47+
namespace Tactic
48+
namespace Alias
49+
50+
open Lean
51+
52+
/-- Adds some copies of a theorem or definition. -/
53+
syntax (name := alias) "alias " ident " ← " ident* : command
54+
55+
/-- Adds one-way implication declarations. -/
56+
syntax (name := aliasLR) "alias " ident " ↔ " binderIdent binderIdent : command
57+
58+
/-- Adds one-way implication declarations, inferring names for them. -/
59+
syntax (name := aliasLRDots) "alias " ident " ↔ " ".." : command
60+
61+
/-- Like `++`, except that if the right argument starts with `_root_` the namespace will be
62+
ignored.
63+
```
64+
appendNamespace `a.b `c.d = `a.b.c.d
65+
appendNamespace `a.b `_root_.c.d = `c.d
66+
```
67+
68+
TODO: Move this declaration to a more central location.
69+
-/
70+
def appendNamespace (ns : Name) : Name → Name
71+
| Name.str Name.anonymous s _ => if s = "_root_" then Name.anonymous else Name.mkStr ns s
72+
| Name.str p s _ => Name.mkStr (appendNamespace ns p) s
73+
| Name.num p n _ => Name.mkNum (appendNamespace ns p) n
74+
| Name.anonymous => ns
75+
76+
/-- Elaborates an `alias ←` command. -/
77+
@[commandElab «alias»] def elabAlias : Elab.Command.CommandElab
78+
| `(alias $name:ident ← $aliases:ident*) => do
79+
let resolved ← resolveGlobalConstNoOverload name
80+
let constantgetConstInfo resolved
81+
let nsgetCurrNamespace
82+
83+
for a in aliases do
84+
let declmatch constant with
85+
| Lean.ConstantInfo.defnInfo d =>
86+
pure $ .defnDecl {
87+
d with name := (appendNamespace ns a.getId)
88+
value := mkConst resolved (d.levelParams.map mkLevelParam)
89+
}
90+
| Lean.ConstantInfo.thmInfo t =>
91+
pure $ .thmDecl {
92+
t with name := (appendNamespace ns a.getId)
93+
value := mkConst resolved (t.levelParams.map mkLevelParam)
94+
}
95+
| _ => throwError "alias only works with def or theorem"
96+
97+
-- TODO add @alias attribute
98+
Lean.addDecl decl
99+
100+
| _ => Lean.Elab.throwUnsupportedSyntax
101+
102+
103+
/--
104+
Given a possibly forall-quantified iff expression `prf`, produce a value for one
105+
of the implication directions (determined by `mp`).
106+
-/
107+
def mkIffMpApp (mp : Bool) (prf : Expr) : MetaM Expr := do
108+
Meta.forallTelescope (← Meta.inferType prf) fun xs ty => do
109+
let some (lhs, rhs) := ty.iff?
110+
| throwError "Target theorem must have the form `∀ x y z, a ↔ b`"
111+
Meta.mkLambdaFVars xs <|
112+
mkApp3 (mkConst (if mp then ``Iff.mp else ``Iff.mpr)) lhs rhs (mkAppN prf xs)
113+
114+
/--
115+
Given a constant representing an iff decl, adds a decl for one of the implication
116+
directions.
117+
-/
118+
def aliasIff (ci : ConstantInfo) (al : Name) (isForward : Bool) : MetaM Unit := do
119+
let ls := ci.levelParams
120+
let v ← mkIffMpApp isForward ci.value!
121+
let t' ← Meta.inferType v
122+
-- TODO add @alias attribute
123+
addDecl $ .thmDecl {
124+
name := al
125+
value := v
126+
type := t'
127+
levelParams := ls
128+
}
129+
130+
/-- Elaborates an `alias ↔` command. -/
131+
@[commandElab aliasLR] def elabAliasLR : Lean.Elab.Command.CommandElab
132+
| `(alias $name:ident ↔ $left:binderIdent $right:binderIdent ) => do
133+
let resolved ← resolveGlobalConstNoOverload name
134+
let constantgetConstInfo resolved
135+
let nsgetCurrNamespace
136+
137+
Lean.Elab.Command.liftTermElabM none do
138+
if let `(binderIdent| $x:ident) := left
139+
then aliasIff constant (appendNamespace ns x.getId) true
140+
141+
if let `(binderIdent| $x:ident) := right
142+
then aliasIff constant (appendNamespace ns x.getId) false
143+
144+
| _ => Lean.Elab.throwUnsupportedSyntax
145+
146+
/-- Elaborates an `alias ↔ ..` command. -/
147+
@[commandElab aliasLRDots] def elabAliasLRDots : Lean.Elab.Command.CommandElab
148+
| `(alias $name:ident ↔ ..) => do
149+
let resolved ← resolveGlobalConstNoOverload name
150+
let constantgetConstInfo resolved
151+
152+
let (parent, base) ← match resolved with
153+
| Name.str n s _ => pure (n,s)
154+
| _ => throwError "alias only works for string names"
155+
156+
let components := base.splitOn "_iff_"
157+
if components.length != 2 then throwError "LHS must be of the form *_iff_*"
158+
let forward := String.intercalate "_of_" components.reverse
159+
let backward := String.intercalate "_of_" components
160+
let forwardName := Name.mkStr parent forward
161+
let backwardName := Name.mkStr parent backward
162+
163+
Lean.Elab.Command.liftTermElabM none do
164+
aliasIff constant forwardName true
165+
aliasIff constant backwardName false
166+
167+
| _ => Lean.Elab.throwUnsupportedSyntax
168+
169+
end Alias
170+
end Tactic

test/Alias.lean

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import Mathlib
2+
3+
namespace Alias
4+
namespace A
5+
6+
theorem foo : 1 + 1 = 2 := rfl
7+
alias foo ← foo1 foo2 foo3 _root_.B.foo4
8+
example : 1 + 1 = 2 := foo1
9+
example : 1 + 1 = 2 := foo2
10+
example : 1 + 1 = 2 := foo3
11+
12+
def bar : Nat := 5
13+
alias bar ← bar1 bar2
14+
example : bar1 = 5 := rfl
15+
example : bar2 = 5 := rfl
16+
17+
theorem baz (x : Nat) : x = x := rfl
18+
alias baz ← baz1
19+
example : 3 = 3 := baz1 3
20+
21+
theorem ab_iff_ba {t : Type} {a b : t} : a = b ↔ b = a := Iff.intro Eq.symm Eq.symm
22+
alias ab_iff_ba ↔ ba_of_ab ab_of_ba
23+
example {a b : Nat} : a = b → b = a := ba_of_ab
24+
example {t : Type} {a b : t} : b = a → a = b := ab_of_ba
25+
26+
theorem a_iff_a_and_a (a : Prop) : a ↔ a ∧ a :=
27+
Iff.intro (λ x => ⟨x,x⟩) (λ x => x.1)
28+
29+
alias a_iff_a_and_a ↔ forward _
30+
alias a_iff_a_and_a ↔ _ backward
31+
32+
example : True → True ∧ True := forward True
33+
example : True ∧ True → True := backward True
34+
35+
alias a_iff_a_and_a ↔ ..
36+
example : True → True ∧ True := a_and_a_of_a True
37+
example : True ∧ True → True := a_of_a_and_a True
38+
39+
end A
40+
41+
-- test namespacing
42+
example : 1 + 1 = 2 := A.foo1
43+
example : 1 + 1 = 2 := B.foo4
44+
example : True → True ∧ True := A.a_and_a_of_a True
45+
example : True → True ∧ True := A.forward True
46+
example : True ∧ True → True := A.backward True
47+
48+
namespace C
49+
50+
alias A.a_iff_a_and_a ↔ _root_.B.forward2 _
51+
alias A.a_iff_a_and_a ↔ _ _root_.B.backward2
52+
53+
end C
54+
55+
example : True → True ∧ True := B.forward2 True
56+
example : True ∧ True → True := B.backward2 True
57+
58+
end Alias

0 commit comments

Comments
 (0)