Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit ee5b38d

Browse files
fpvandoornmergify[bot]
authored andcommitted
feat(simps): allow the user to specify the projections (#1630)
* feat(simps): allow the user to specify the projections Also add option to shorten generated lemma names Add the attribute to more places in the category_theory library The projection lemmas of inl_ and inr_ are now called inl__obj and similar * use simps partially in limits/cones and whiskering * revert whiskering * rename last_name to short_name * Update src/category_theory/products/basic.lean * Update src/category_theory/limits/cones.lean Co-Authored-By: Scott Morrison <scott@tqft.net> * Update src/category_theory/products/associator.lean Co-Authored-By: Scott Morrison <scott@tqft.net> * Update src/data/string/defs.lean Co-Authored-By: Scott Morrison <scott@tqft.net> * clarify is_eta_expansion docstrings
1 parent a6ace34 commit ee5b38d

File tree

11 files changed

+200
-139
lines changed

11 files changed

+200
-139
lines changed

docs/tactics.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,7 +1175,7 @@ attribute [simp, reassoc] some_class.bar
11751175
Instead of creating a new assumption from the result, `reassoc_of h` stands for the proof of that reassociated
11761176
statement. This prevents poluting the local context with complicated assumptions used only once or twice.
11771177

1178-
In the following, assumption `h` is needed in a reassociated form. Instead of proving it as a new goal and adding it as
1178+
In the following, assumption `h` is needed in a reassociated form. Instead of proving it as a new goal and adding it as
11791179
an assumption, we use `reassoc_of h` as a rewrite rule which works just as well.
11801180

11811181
```lean
@@ -1189,7 +1189,7 @@ begin
11891189
end
11901190
```
11911191

1192-
Although `reassoc_of` is not a tactic or a meta program, its type is generated
1192+
Although `reassoc_of` is not a tactic or a meta program, its type is generated
11931193
through meta-programming to make it usable inside normal expressions.
11941194

11951195
### lint
@@ -1283,24 +1283,32 @@ See also additional documentation of `using_well_founded` in
12831283
### simps
12841284

12851285
* The `@[simps]` attribute automatically derives lemmas specifying the projections of the declaration.
1286-
* Example:
1286+
* Example: (note that the forward and reverse functions are specified differently!)
12871287
```lean
1288-
@[simps] def refl (α) : α ≃ α := ⟨id, id, λ x, rfl, λ x, rfl⟩
1288+
@[simps] def refl (α) : α ≃ α := ⟨id, λ x, x, λ x, rfl, λ x, rfl⟩
12891289
```
12901290
derives two simp-lemmas:
12911291
```lean
1292-
@[simp] lemma refl_to_fun (α) : (refl α).to_fun = id
1293-
@[simp] lemma refl_inv_fun (α) : (refl α).inv_fun = id
1292+
@[simp] lemma refl_to_fun (α) (x : α) : (refl α).to_fun x = id x
1293+
@[simp] lemma refl_inv_fun (α) (x : α) : (refl α).inv_fun x = x
12941294
```
12951295
* It does not derive simp-lemmas for the prop-valued projections.
12961296
* It will automatically reduce newly created beta-redexes, but not unfold any definitions.
12971297
* If one of the fields itself is a structure, this command will recursively create
12981298
simp-lemmas for all fields in that structure.
1299+
* You can use `@[simps proj1 proj2 ...]` to only generate the projection lemmas for the specified
1300+
projections. For example:
1301+
```lean
1302+
attribute [simps to_fun] refl
1303+
```
12991304
* If one of the values is an eta-expanded structure, we will eta-reduce this structure.
13001305
* You can use `@[simps lemmas_only]` to derive the lemmas, but not mark them
13011306
as simp-lemmas.
1307+
* You can use `@[simps short_name]` to only use the name of the last projection for the name of the
1308+
generated lemmas.
1309+
* The precise syntax is `('simps' 'lemmas_only'? 'short_name'? ident*)`.
13021310
* If one of the projections is marked as a coercion, the generated lemmas do *not* use this
13031311
coercion.
1312+
* `@[simps]` reduces let-expressions where necessary.
13041313
* If one of the fields is a partially applied constructor, we will eta-expand it
13051314
(this likely never happens).
1306-
* `@[simps]` reduces let-expressions where necessary.

src/category_theory/limits/cones.lean

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -377,46 +377,36 @@ namespace category_theory.limits
377377

378378
variables {F : J ⥤ Cᵒᵖ}
379379

380-
def cone_of_cocone_left_op (c : cocone F.left_op) : cone F :=
380+
-- Here and below we only automatically generate the `@[simp]` lemma for the `X` field,
381+
-- as we can be a simpler `rfl` lemma for the components of the natural transformation by hand.
382+
@[simps X] def cone_of_cocone_left_op (c : cocone F.left_op) : cone F :=
381383
{ X := op c.X,
382384
π := nat_trans.right_op (c.ι ≫ (const.op_obj_unop (op c.X)).hom) }
383385

384-
@[simp] lemma cone_of_cocone_left_op_X (c : cocone F.left_op) :
385-
(cone_of_cocone_left_op c).X = op c.X :=
386-
rfl
387386
@[simp] lemma cone_of_cocone_left_op_π_app (c : cocone F.left_op) (j) :
388387
(cone_of_cocone_left_op c).π.app j = (c.ι.app (op j)).op :=
389388
by { dsimp [cone_of_cocone_left_op], simp }
390389

391-
def cocone_left_op_of_cone (c : cone F) : cocone (F.left_op) :=
390+
@[simps X] def cocone_left_op_of_cone (c : cone F) : cocone (F.left_op) :=
392391
{ X := unop c.X,
393392
ι := nat_trans.left_op c.π }
394393

395-
@[simp] lemma cocone_left_op_of_cone_X (c : cone F) :
396-
(cocone_left_op_of_cone c).X = unop c.X :=
397-
rfl
398394
@[simp] lemma cocone_left_op_of_cone_ι_app (c : cone F) (j) :
399395
(cocone_left_op_of_cone c).ι.app j = (c.π.app (unop j)).unop :=
400396
by { dsimp [cocone_left_op_of_cone], simp }
401397

402-
def cocone_of_cone_left_op (c : cone F.left_op) : cocone F :=
398+
@[simps X] def cocone_of_cone_left_op (c : cone F.left_op) : cocone F :=
403399
{ X := op c.X,
404400
ι := nat_trans.right_op ((const.op_obj_unop (op c.X)).hom ≫ c.π) }
405401

406-
@[simp] lemma cocone_of_cone_left_op_X (c : cone F.left_op) :
407-
(cocone_of_cone_left_op c).X = op c.X :=
408-
rfl
409402
@[simp] lemma cocone_of_cone_left_op_ι_app (c : cone F.left_op) (j) :
410403
(cocone_of_cone_left_op c).ι.app j = (c.π.app (op j)).op :=
411404
by { dsimp [cocone_of_cone_left_op], simp }
412405

413-
def cone_left_op_of_cocone (c : cocone F) : cone (F.left_op) :=
406+
@[simps X] def cone_left_op_of_cocone (c : cocone F) : cone (F.left_op) :=
414407
{ X := unop c.X,
415408
π := nat_trans.left_op c.ι }
416409

417-
@[simp] lemma cone_left_op_of_cocone_X (c : cocone F) :
418-
(cone_left_op_of_cocone c).X = unop c.X :=
419-
rfl
420410
@[simp] lemma cone_left_op_of_cocone_π_app (c : cocone F) (j) :
421411
(cone_left_op_of_cocone c).π.app j = (c.ι.app (unop j)).unop :=
422412
by { dsimp [cone_left_op_of_cocone], simp }

src/category_theory/products/associator.lean

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,16 @@ variables (C : Type u₁) [𝒞 : category.{v₁} C]
2020
(E : Type u₃) [ℰ : category.{v₃} E]
2121
include 𝒞 𝒟 ℰ
2222

23-
def associator : ((C × D) × E) ⥤ (C × (D × E)) :=
23+
-- Here and below we specify explicitly the projections to generate `@[simp]` lemmas for,
24+
-- as the default behaviour of `@[simps]` will generate projections all the way down to components of pairs.
25+
@[simps obj map] def associator : ((C × D) × E) ⥤ (C × (D × E)) :=
2426
{ obj := λ X, (X.1.1, (X.1.2, X.2)),
2527
map := λ _ _ f, (f.1.1, (f.1.2, f.2)) }
2628

27-
@[simp] lemma associator_obj (X) :
28-
(associator C D E).obj X = (X.1.1, (X.1.2, X.2)) :=
29-
rfl
30-
@[simp] lemma associator_map {X Y} (f : X ⟶ Y) :
31-
(associator C D E).map f = (f.1.1, (f.1.2, f.2)) :=
32-
rfl
33-
34-
def inverse_associator : (C × (D × E)) ⥤ ((C × D) × E) :=
29+
@[simps obj map] def inverse_associator : (C × (D × E)) ⥤ ((C × D) × E) :=
3530
{ obj := λ X, ((X.1, X.2.1), X.2.2),
3631
map := λ _ _ f, ((f.1, f.2.1), f.2.2) }
3732

38-
@[simp] lemma inverse_associator_obj (X) :
39-
(inverse_associator C D E).obj X = ((X.1, X.2.1), X.2.2) :=
40-
rfl
41-
@[simp] lemma inverse_associator_map {X Y} (f : X ⟶ Y) :
42-
(inverse_associator C D E).map f = ((f.1, f.2.1), f.2.2) :=
43-
rfl
44-
4533
def associativity : (C × D) × E ≌ C × (D × E) :=
4634
equivalence.mk (associator C D E) (inverse_associator C D E)
4735
(nat_iso.of_components (λ X, eq_to_iso (by simp)) (by tidy))

src/category_theory/products/basic.lean

Lines changed: 13 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -51,45 +51,32 @@ variables (C : Type u₁) [𝒞 : category.{v₁} C] (D : Type u₂) [𝒟 : cat
5151
include 𝒞 𝒟
5252

5353
/-- `inl C Z` is the functor `X ↦ (X, Z)`. -/
54-
def inl (Z : D) : C ⥤ C × D :=
54+
-- Here and below we specify explicitly the projections to generate `@[simp]` lemmas for,
55+
-- as the default behaviour of `@[simps]` will generate projections all the way down to components of pairs.
56+
@[simps obj map] def inl (Z : D) : C ⥤ C × D :=
5557
{ obj := λ X, (X, Z),
5658
map := λ X Y f, (f, 𝟙 Z) }
5759

58-
@[simp] lemma inl_obj (Z : D) (X : C) : (inl C D Z).obj X = (X, Z) := rfl
59-
@[simp] lemma inl_map (Z : D) {X Y : C} {f : X ⟶ Y} : (inl C D Z).map f = (f, 𝟙 Z) := rfl
60-
6160
/-- `inr D Z` is the functor `X ↦ (Z, X)`. -/
62-
def inr (Z : C) : D ⥤ C × D :=
61+
@[simps obj map] def inr (Z : C) : D ⥤ C × D :=
6362
{ obj := λ X, (Z, X),
6463
map := λ X Y f, (𝟙 Z, f) }
6564

66-
@[simp] lemma inr_obj (Z : C) (X : D) : (inr C D Z).obj X = (Z, X) := rfl
67-
@[simp] lemma inr_map (Z : C) {X Y : D} {f : X ⟶ Y} : (inr C D Z).map f = (𝟙 Z, f) := rfl
68-
6965
/-- `fst` is the functor `(X, Y) ↦ X`. -/
70-
def fst : C × D ⥤ C :=
66+
@[simps obj map] def fst : C × D ⥤ C :=
7167
{ obj := λ X, X.1,
7268
map := λ X Y f, f.1 }
7369

74-
@[simp] lemma fst_obj (X : C × D) : (fst C D).obj X = X.1 := rfl
75-
@[simp] lemma fst_map {X Y : C × D} {f : X ⟶ Y} : (fst C D).map f = f.1 := rfl
76-
7770
/-- `snd` is the functor `(X, Y) ↦ Y`. -/
78-
def snd : C × D ⥤ D :=
71+
@[simps obj map] def snd : C × D ⥤ D :=
7972
{ obj := λ X, X.2,
8073
map := λ X Y f, f.2 }
8174

82-
@[simp] lemma snd_obj (X : C × D) : (snd C D).obj X = X.2 := rfl
83-
@[simp] lemma snd_map {X Y : C × D} {f : X ⟶ Y} : (snd C D).map f = f.2 := rfl
84-
85-
def swap : C × D ⥤ D × C :=
75+
@[simps obj map] def swap : C × D ⥤ D × C :=
8676
{ obj := λ X, (X.2, X.1),
8777
map := λ _ _ f, (f.2, f.1) }
8878

89-
@[simp] lemma swap_obj (X : C × D) : (swap C D).obj X = (X.2, X.1) := rfl
90-
@[simp] lemma swap_map {X Y : C × D} {f : X ⟶ Y} : (swap C D).map f = (f.2, f.1) := rfl
91-
92-
def symmetry : swap C D ⋙ swap D C ≅ 𝟭 (C × D) :=
79+
@[simps hom_app inv_app] def symmetry : swap C D ⋙ swap D C ≅ 𝟭 (C × D) :=
9380
{ hom := { app := λ X, 𝟙 X },
9481
inv := { app := λ X, 𝟙 X } }
9582

@@ -107,21 +94,15 @@ section
10794
variables (C : Type u₁) [𝒞 : category.{v₁} C] (D : Type u₂) [𝒟 : category.{v₂} D]
10895
include 𝒞 𝒟
10996

110-
def evaluation : C ⥤ (C ⥤ D) ⥤ D :=
97+
@[simps] def evaluation : C ⥤ (C ⥤ D) ⥤ D :=
11198
{ obj := λ X,
11299
{ obj := λ F, F.obj X,
113100
map := λ F G α, α.app X, },
114101
map := λ X Y f,
115102
{ app := λ F, F.map f,
116103
naturality' := λ F G α, eq.symm (α.naturality f) } }
117104

118-
@[simp] lemma evaluation_obj_obj (X) (F) : ((evaluation C D).obj X).obj F = F.obj X := rfl
119-
@[simp] lemma evaluation_obj_map (X) {F G} (α : F ⟶ G) :
120-
((evaluation C D).obj X).map α = α.app X := rfl
121-
@[simp] lemma evaluation_map_app {X Y} (f : X ⟶ Y) (F) :
122-
((evaluation C D).map f).app F = F.map f := rfl
123-
124-
def evaluation_uncurried : C × (C ⥤ D) ⥤ D :=
105+
@[simps obj map] def evaluation_uncurried : C × (C ⥤ D) ⥤ D :=
125106
{ obj := λ p, p.2.obj p.1,
126107
map := λ x y f, (x.2.map f.1) ≫ (f.2.app y.1),
127108
map_comp' := λ X Y Z f g,
@@ -132,10 +113,6 @@ def evaluation_uncurried : C × (C ⥤ D) ⥤ D :=
132113
category.assoc, nat_trans.naturality],
133114
end }
134115

135-
@[simp] lemma evaluation_uncurried_obj (p) : (evaluation_uncurried C D).obj p = p.2.obj p.1 := rfl
136-
@[simp] lemma evaluation_uncurried_map {x y} (f : x ⟶ y) :
137-
(evaluation_uncurried C D).map f = (x.2.map f.1) ≫ (f.2.app y.1) := rfl
138-
139116
end
140117

141118
variables {A : Type u₁} [𝒜 : category.{v₁} A]
@@ -146,23 +123,20 @@ include 𝒜 ℬ 𝒞 𝒟
146123

147124
namespace functor
148125
/-- The cartesian product of two functors. -/
149-
def prod (F : A ⥤ B) (G : C ⥤ D) : A × C ⥤ B × D :=
126+
@[simps obj map] def prod (F : A ⥤ B) (G : C ⥤ D) : A × C ⥤ B × D :=
150127
{ obj := λ X, (F.obj X.1, G.obj X.2),
151128
map := λ _ _ f, (F.map f.1, G.map f.2) }
152129

153130
/- Because of limitations in Lean 3's handling of notations, we do not setup a notation `F × G`.
154131
You can use `F.prod G` as a "poor man's infix", or just write `functor.prod F G`. -/
155132

156-
@[simp] lemma prod_obj (F : A ⥤ B) (G : C ⥤ D) (a : A) (c : C) :
157-
(F.prod G).obj (a, c) = (F.obj a, G.obj c) := rfl
158-
@[simp] lemma prod_map (F : A ⥤ B) (G : C ⥤ D) {a a' : A} {c c' : C} (f : (a, c) ⟶ (a', c')) :
159-
(F.prod G).map f = (F.map f.1, G.map f.2) := rfl
160133
end functor
161134

162135
namespace nat_trans
163136

164137
/-- The cartesian product of two natural transformations. -/
165-
def prod {F G : A ⥤ B} {H I : C ⥤ D} (α : F ⟶ G) (β : H ⟶ I) : F.prod H ⟶ G.prod I :=
138+
@[simps app] def prod {F G : A ⥤ B} {H I : C ⥤ D} (α : F ⟶ G) (β : H ⟶ I) :
139+
F.prod H ⟶ G.prod I :=
166140
{ app := λ X, (α.app X.1, β.app X.2),
167141
naturality' := λ X Y f,
168142
begin
@@ -174,8 +148,6 @@ def prod {F G : A ⥤ B} {H I : C ⥤ D} (α : F ⟶ G) (β : H ⟶ I) : F.prod
174148
/- Again, it is inadvisable in Lean 3 to setup a notation `α × β`;
175149
use instead `α.prod β` or `nat_trans.prod α β`. -/
176150

177-
@[simp] lemma prod_app {F G : A ⥤ B} {H I : C ⥤ D} (α : F ⟶ G) (β : H ⟶ I) (a : A) (c : C) :
178-
(nat_trans.prod α β).app (a, c) = (α.app a, β.app c) := rfl
179151
end nat_trans
180152

181153
end category_theory

src/category_theory/sums/basic.lean

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,15 @@ include 𝒞 𝒟
5555

5656
/-- `inl_` is the functor `X ↦ inl X`. -/
5757
-- Unfortunate naming here, suggestions welcome.
58-
def inl_ : C ⥤ C ⊕ D :=
58+
@[simps] def inl_ : C ⥤ C ⊕ D :=
5959
{ obj := λ X, inl X,
6060
map := λ X Y f, f }
6161

62-
@[simp] lemma inl_obj (X : C) : (inl_ C D).obj X = inl X := rfl
63-
@[simp] lemma inl_map {X Y : C} {f : X ⟶ Y} : (inl_ C D).map f = f := rfl
64-
6562
/-- `inr_` is the functor `X ↦ inr X`. -/
66-
def inr_ : D ⥤ C ⊕ D :=
63+
@[simps] def inr_ : D ⥤ C ⊕ D :=
6764
{ obj := λ X, inr X,
6865
map := λ X Y f, f }
6966

70-
@[simp] lemma inr_obj (X : D) : (inr_ C D).obj X = inr X := rfl
71-
@[simp] lemma inr_map {X Y : D} {f : X ⟶ Y} : (inr_ C D).map f = f := rfl
72-
7367
/-- The functor exchanging two direct summand categories. -/
7468
def swap : C ⊕ D ⥤ D ⊕ C :=
7569
{ obj :=

src/data/list/defs.lean

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,4 +531,11 @@ protected def traverse {F : Type u → Type v} [applicative F] {α β : Type*} (
531531
| [] := pure []
532532
| (x :: xs) := list.cons <$> f x <*> traverse xs
533533

534+
/-- `get_rest l l₁` returns `some l₂` if `l = l₁ ++ l₂`.
535+
If `l₁` is not a prefix of `l`, returns `none` -/
536+
def get_rest [decidable_eq α] : list α → list α → option (list α)
537+
| l [] := some l
538+
| [] _ := none
539+
| (x::l) (y::l₁) := if x = y then get_rest l l₁ else none
540+
534541
end list

src/data/string/defs.lean

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,15 @@ def split_on (c : char) (s : string) : list string :=
1515
def is_prefix_of (x y : string) : bool :=
1616
x.to_list.is_prefix_of y.to_list
1717

18+
/-- Tests whether the first string is a suffix of the second string -/
1819
def is_suffix_of (x y : string) : bool :=
1920
x.to_list.is_suffix_of y.to_list
2021

22+
/-- `get_rest s t` returns `some r` if `s = t ++ r`.
23+
If `t` is not a prefix of `s`, returns `none` -/
24+
def get_rest (s t : string) : option string :=
25+
list.as_string <$> s.to_list.get_rest t.to_list
26+
2127
/-- Removes the first `n` elements from the string `s` -/
2228
def popn (s : string) (n : nat) : string :=
2329
(s.mk_iterator.nextn n).next_to_string

src/meta/expr.lean

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -475,14 +475,18 @@ namespace expr
475475
is an eta-expansion of a structure. (not to be confused with eta-expanion for `λ`). -/
476476
open tactic
477477

478-
/-- Checks whether for all elements `(nm, val)` in the list we have `val = nm.{univs} args` -/
478+
/-- `is_eta_expansion_of args univs l` checks whether for all elements `(nm, pr)` in `l` we have
479+
`pr = nm.{univs} args`.
480+
Used in `is_eta_expansion`, where `l` consists of the projections and the fields of the value we
481+
want to eta-reduce. -/
479482
meta def is_eta_expansion_of (args : list expr) (univs : list level) (l : list (name × expr)) :
480483
bool :=
481484
l.all $ λ⟨proj, val⟩, val = (const proj univs).mk_app args
482485

483-
/-- Checks whether there is an expression `e` such that for all elements `(nm, val)` in the list
484-
`val = nm ... e` where `...` denotes the same list of parameters for all applications.
485-
Returns e if it exists. -/
486+
/-- `is_eta_expansion_test l` checks whether there is a list of expresions `args` such that for all
487+
elements `(nm, pr)` in `l` we have `pr = nm args`. If so, returns the last element of `args`.
488+
Used in `is_eta_expansion`, where `l` consists of the projections and the fields of the value we
489+
want to eta-reduce. -/
486490
meta def is_eta_expansion_test : list (name × expr) → option expr
487491
| [] := none
488492
| (⟨proj, val⟩::l) :=
@@ -497,17 +501,22 @@ meta def is_eta_expansion_test : list (name × expr) → option expr
497501
| _ := none
498502
end
499503

500-
/-- Checks whether there is an expression `e` such that for all *non-propositional* elements
501-
`(nm, val)` in the list `val = e ... nm` where `...` denotes the same list of parameters for all
502-
applications. Also checks whether the resulting expression unifies with the given one -/
504+
/-- `is_eta_expansion_aux val l` checks whether `val` can be eta-reduced to an expression `e`.
505+
Here `l` is intended to consists of the projections and the fields of `val`.
506+
This tactic calls `is_eta_expansion_test l`, but first removes all proofs from the list `l` and
507+
afterward checks whether the retulting expression `e` unifies with `val`.
508+
This last check is necessary, because `val` and `e` might have different types. -/
503509
meta def is_eta_expansion_aux (val : expr) (l : list (name × expr)) : tactic (option expr) :=
504510
do l' ← l.mfilter (λ⟨proj, val⟩, bnot <$> is_proof val),
505511
match is_eta_expansion_test l' with
506512
| some e := option.map (λ _, e) <$> try_core (unify e val)
507513
| none := return none
508514
end
509515

510-
/-- Checks whether there is an expression `e` such that `val` is the eta-expansion of `e`.
516+
/-- `is_eta_expansion val` checks whether there is an expression `e` such that `val` is the
517+
eta-expansion of `e`.
518+
With eta-expansion we here mean the eta-expansion of a structure, not of a function.
519+
For example, the eta-expansion of `x : α × β` is `⟨x.1, x.2⟩`.
511520
This assumes that `val` is a fully-applied application of the constructor of a structure.
512521
513522
This is useful to reduce expressions generated by the notation

src/tactic/core.lean

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,8 +1107,9 @@ and fails otherwise.
11071107
meta def {u} success_if_fail_with_msg {α : Type u} (t : tactic α) (msg : string) : tactic unit :=
11081108
λ s, match t s with
11091109
| (interaction_monad.result.exception msg' _ s') :=
1110-
if msg = (msg'.iget ()).to_string then result.success () s
1111-
else mk_exception "failure messages didn't match" none s
1110+
let expected_msg := (msg'.iget ()).to_string in
1111+
if msg = expected_msg then result.success () s
1112+
else mk_exception format!"failure messages didn't match. Expected:\n{expected_msg}" none s
11121113
| (interaction_monad.result.success a s) :=
11131114
mk_exception "success_if_fail_with_msg combinator failed, given tactic succeeded" none s
11141115
end

0 commit comments

Comments
 (0)