Zulip Chat Archive
Stream: general
Topic: Need help with class instance resolution
Yury G. Kudryashov (Mar 31 2020 at 17:20):
Hi, I'm trying to make algebra
work with semirings and semimodules, see #2303. Lean fails to find an instance of algebra R (matrix n R)
in lie_algebra.lean
. I've enables trace.class_instances
, see build log. It seems that matrix_algebra
fails defeq
. Why could this happen?
Kevin Buzzard (Mar 31 2020 at 17:22):
import algebra.lie_algebra example (R : Type) [comm_ring R] (n : ℕ) : algebra R (matrix (fin n) (fin n) R) := by apply_instance -- works example (R : Type) [comm_ring R] (n : Type) [fintype n] : algebra R (matrix n n R) := by apply_instance -- fails
What definition of matrix are you using? (what you posted doesn't typecheck for me)
Yury G. Kudryashov (Mar 31 2020 at 17:26):
I'm talking about the bottom of lie_algebra.lean
Kevin Buzzard (Mar 31 2020 at 17:27):
matrix n n R
Yury G. Kudryashov (Mar 31 2020 at 17:27):
It compiles with master
but fails in my branch, probably because of some semiring
vs ring
or semimodule
vs module
issues.
Yury G. Kudryashov (Mar 31 2020 at 17:28):
In the build log you can see that it tries to apply matrix_algebra
but fails.
Kevin Buzzard (Mar 31 2020 at 17:30):
The above doesn't compile for me on master:
failed to synthesize type class instance for R : Type, _inst_1 : comm_ring R, n : Type, _inst_2 : fintype n ⊢ ring (matrix n n R)
Yury G. Kudryashov (Mar 31 2020 at 17:31):
That's interesting.
Kevin Buzzard (Mar 31 2020 at 17:32):
import algebra.lie_algebra def X : ℕ := 3 open_locale classical noncomputable example (R : Type) [comm_ring R] (n : Type) [fintype n] : algebra R (matrix n n R) := by apply_instance
Kevin Buzzard (Mar 31 2020 at 17:32):
Need decidable equality on n
Kevin Buzzard (Mar 31 2020 at 17:33):
(the def to work around open_locale bug)
Yury G. Kudryashov (Mar 31 2020 at 17:34):
Don't we have a decidable equality on fin n
? And we have it at the bottom of lie_algebra
Yury G. Kudryashov (Mar 31 2020 at 17:35):
You've posted code snippet with fin n
, then error for {n : Type*} [fintype n]
Kevin Buzzard (Mar 31 2020 at 17:44):
Yes, I was confused by n. I'll fix. Thanks.
Yury G. Kudryashov (Mar 31 2020 at 17:57):
And this doesn't help with the original question: why lie_algebra
fails to compile in my branch?
Yury G. Kudryashov (Mar 31 2020 at 18:14):
Probably this issue is similar to what @Sebastien Gouezel had when he tried to make linear_map
s work with semimodule
s
Yury G. Kudryashov (Mar 31 2020 at 19:24):
Ping here...
Yury G. Kudryashov (Mar 31 2020 at 20:07):
@Reid Barton I'd prefer some hint...
Sebastien Gouezel (Apr 02 2020 at 15:58):
I have tried (again) to refactor things to replace module
by semimodule
everywhere, hoping that Lean 3.7 would help. I run into issues I don't understand (mostly with product types), with class instance resolution as usual. I have tracked down the source of my problem to the following example. Open current mathlib master, and put the following lines at Line 85 in algebra/pi_instances.lean
(after the semimodule instance, but before the module instance)
lemma ok (α : Type) [ring α] (β : Type) : @semimodule.{0 0} α (β → α) (@ring.to_semiring.{0} α _inst_1) (@pi.add_comm_monoid.{0 0} β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid.{0} α (@ring.to_semiring.{0} α _inst_1))) := by apply_instance -- ok lemma not_ok (α : Type) [ring α] (β : Type) : @semimodule.{0 0} α (β → α) (@ring.to_semiring.{0} α _inst_1) (@add_comm_group.to_add_comm_monoid.{0} (β → α) (@pi.add_comm_group.{0 0} β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group.{0} α _inst_1))) := by apply_instance -- fails lemma but_same (α : Type) [ring α] (β : Type) : @semimodule.{0 0} α (β → α) (@ring.to_semiring.{0} α _inst_1) (@pi.add_comm_monoid.{0 0} β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid.{0} α (@ring.to_semiring.{0} α _inst_1))) = @semimodule.{0 0} α (β → α) (@ring.to_semiring.{0} α _inst_1) (@add_comm_group.to_add_comm_monoid.{0} (β → α) (@pi.add_comm_group.{0 0} β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group.{0} α _inst_1))) := rfl
In ok
and not_ok
, I am asking if the same type, but presented in two different ways, is a semimodule. The first one works, the second one fails. The last lemma in the snippet is to show that the types are really the same. In current master, the following module instance saves the day, because it can then be used to show that not_ok
is a semimodule, so when this class instance question appears later on it is solved. In my refactoring, I remove modules, so I remove the pi_instance
for modules, and now this instance fails. Of course, I could add it back (with semimodules), but it doesn't feel like a real solution. Do you understand what is going on, and what I should do here?
Sebastien Gouezel (Apr 02 2020 at 17:34):
Anyone on this typeclass issue? Maybe I should try to bring @Daniel Selsam here.
Kevin Buzzard (Apr 02 2020 at 17:41):
(@add_comm_group.to_add_comm_monoid.{0} (β → α) (@pi.add_comm_group.{0 0} β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group.{0} α _inst_1)))
has type add_comm_monoid (β → α)
, whereas
(@pi.add_comm_monoid.{0 0} β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid.{0} α (@ring.to_semiring.{0} α _inst_1)))
has type add_comm_monoid (Π (i : β), (λ (a : β), α) i)
Sebastien Gouezel (Apr 02 2020 at 17:42):
And these are the same thing, right?
Kevin Buzzard (Apr 02 2020 at 17:42):
Maybe not to the unifier
Sebastien Gouezel (Apr 02 2020 at 17:45):
At least some parts of Lean know it is the same thing:
lemma foo {α : Type*} {β : Type*} : (β → α) = (Π (i : β), (λ (a : β), α) i) := rfl
Kevin Buzzard (Apr 02 2020 at 17:45):
For sure, but you know that rw
doesn't think they're the same thing
Kevin Buzzard (Apr 02 2020 at 17:46):
I don't know the answer, I've just taken to looking at the output of
set_option trace.type_context.is_def_eq true set_option trace.type_context.is_def_eq_detail true
recently.
Kevin Buzzard (Apr 02 2020 at 17:49):
foo
is iota reduction or eta expansion or whatever it is called by the CS people, and that's part of rfl
but maybe not part of the unifier. But also it might be nothing to do with the problem :-(
Kevin Buzzard (Apr 02 2020 at 17:51):
Actually rw seems to work fine too
Kevin Buzzard (Apr 02 2020 at 17:51):
I don't know how to check to see if two terms are syntactically equal in Lean
Kevin Buzzard (Apr 02 2020 at 17:51):
example (α : Type) [ring α] (β : Type) : β → α := begin have h : (Π (i : β), (λ (a : β), α) i) = (Π (i : β), (λ (a : β), α) i) := rfl, rw h, -- works sorry end
Sebastien Gouezel (Apr 02 2020 at 17:52):
This is the kind of problem where I feel completely helpless. When even with pp.all
I can't see anything bad, I just need to call for help...
Kevin Buzzard (Apr 02 2020 at 17:53):
Yeah I am interested in learning how to debug these issues. mv_polynomial
is defined to be some finsupp
after some unfolding, and I saw an mv_polynomial zero and a finsupp zero which add_zero
got upset about because of some unification issue but refl
worked fine.
Kevin Buzzard (Apr 02 2020 at 17:57):
pp.all
just shows these defeq terms. The question is what the type class inference algorithm is I guess.
Kevin Buzzard (Apr 02 2020 at 18:03):
With set_option trace.class_instances true
, the success
def ok : @semimodule.{0 0} α (β → α) (@ring.to_semiring.{0} α _inst_1) (@pi.add_comm_monoid.{0 0} β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid.{0} α (@ring.to_semiring.{0} α _inst_1))) := begin apply_instance end -- ok
has log
[class_instances] (0) ?x_0 : @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@pi.add_comm_monoid β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid α (@ring.to_semiring α _inst_1))) := @pi.semimodule ?x_1 ?x_2 ?x_3 ?x_4 ?x_5 ?x_6 [class_instances] (1) ?x_6 i : @semimodule α α (@ring.to_semiring α _inst_1) (@semiring.to_add_comm_monoid α (@ring.to_semiring α _inst_1)) := @semiring.to_semimodule (?x_15 i) (?x_16 i)
Kevin Buzzard (Apr 02 2020 at 18:05):
(I skipped a couple of steps and just showed the successful route to the success) and the failure has a longer log.
Kevin Buzzard (Apr 02 2020 at 18:08):
The very first line of the gist is
[class_instances] (0) ?x_0 : @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@add_comm_group.to_add_comm_monoid (β → α) (@pi.add_comm_group β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group α _inst_1))) := @pi.semimodule ?x_1 ?x_2 ?x_3 ?x_4 ?x_5 ?x_6 failed is_def_eq
so it tries to use pi.semimodule
but here it fails.
Kevin Buzzard (Apr 02 2020 at 18:09):
I think this is the precise point where the typeclass algorithm decides to go in two different ways for your two different questions.
Kevin Buzzard (Apr 02 2020 at 18:13):
variables (α : Type) [ring α] (β : Type) def ok' : @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@pi.add_comm_monoid β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid α (@ring.to_semiring α _inst_1))) := begin refine @pi.semimodule _ _ _ _ _ _, -- Proof complete! end def not_ok' : @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@add_comm_group.to_add_comm_monoid (β → α) (@pi.add_comm_group β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group α _inst_1))) := begin refine @pi.semimodule _ _ _ _ _ _, -- fails /- invalid type ascription, term has type semimodule ?m_1 (Π (i : ?m_2), ?m_3 i) : Type (max ? ? ?) but is expected to have type semimodule α (β → α) : Type -/ end
Kevin Buzzard (Apr 02 2020 at 18:13):
I think @Reid Barton and @Gabriel Ebner were talking about this kind of unification issue quite recently.
Kevin Buzzard (Apr 02 2020 at 18:16):
The types of ok'
and not_ok'
are defeq.
Kevin Buzzard (Apr 02 2020 at 18:18):
Of course I am in no position to be able to tell you what to do about it, I'm just observing that with set_option trace.class_instances true
you can diagnose what is going on even if you know as little about computers as me.
Kevin Buzzard (Apr 02 2020 at 18:22):
The first difference in the trace.type_context.is_def_eq true
logs is that in the OK case, we have
[type_context.is_def_eq] semimodule ?m_1 (Π (i : ?m_2), ?m_3 i) =?= semimodule α (β → α) ... success (approximate mode)
and in the not OK case, we have
[type_context.is_def_eq] semimodule ?m_1 (Π (i : ?m_2), ?m_3 i) =?= semimodule α (β → α) ... failed (approximate mode)
So that is a diagnosis of the failure, which might help someone else to tell you how to fix it.
Reid Barton (Apr 02 2020 at 18:23):
These lines are the same up to success/failed
, right?
Kevin Buzzard (Apr 02 2020 at 18:23):
Yes, but I didn't have the detailed version on
Reid Barton (Apr 02 2020 at 18:24):
are these logs with pp.all? is that what you mean?
Kevin Buzzard (Apr 02 2020 at 18:24):
oh no, i have pp.all off
Kevin Buzzard (Apr 02 2020 at 18:26):
I meant I didn't have trace.type_context.is_def_eq_detail
on
Reid Barton (Apr 02 2020 at 18:26):
Well, I imagine/hope there must be some difference between the pp.all versions.
Reid Barton (Apr 02 2020 at 18:26):
Ah.
Kevin Buzzard (Apr 02 2020 at 18:28):
Here is the pp.all unification failure:
[type_context.is_def_eq] @semimodule.{?l_1 (max ?l_2 ?l_3)} ?m_4 (Π (i : ?m_5), ?m_6 i) ?m_7 (@pi.add_comm_monoid.{?l_2 ?l_3} ?m_5 (λ (i : ?m_5), ?m_6 i) (λ (i : ?m_5), ?m_8 i)) =?= @semimodule.{0 0} α (β → α) (@ring.to_semiring.{0} α _inst_1) (@add_comm_group.to_add_comm_monoid.{0} (β → α) (@pi.add_comm_group.{0 0} β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group.{0} α _inst_1))) ... failed (approximate mode)
and here is the success:
[type_context.is_def_eq] @semimodule.{?l_1 (max ?l_2 ?l_3)} ?m_4 (Π (i : ?m_5), ?m_6 i) ?m_7 (@pi.add_comm_monoid.{?l_2 ?l_3} ?m_5 (λ (i : ?m_5), ?m_6 i) (λ (i : ?m_5), ?m_8 i)) =?= @semimodule.{0 0} α (β → α) (@ring.to_semiring.{0} α _inst_1) (@pi.add_comm_monoid.{0 0} β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid.{0} α (@ring.to_semiring.{0} α _inst_1))) ... success (approximate mode)
Kevin Buzzard (Apr 02 2020 at 18:29):
So now you can all play the unification game at home
Reid Barton (Apr 02 2020 at 18:30):
Okay so there is a diamond here, when putting an add_comm_monoid structure on a product of rings.
Reid Barton (Apr 02 2020 at 18:31):
we could (1) forget from ring to add_comm_group, (2) use the pi instance, (3) forget from add_comm_group to add_comm_monoid.
Or (1) forget from ring to semiring, (2) forget from ring to add_comm_monoid, (3) use the pi instance.
Reid Barton (Apr 02 2020 at 18:31):
question 1: are these defeq?
Reid Barton (Apr 02 2020 at 18:31):
I'm pretty sure the answer will be yes
Reid Barton (Apr 02 2020 at 18:33):
question 2: are they defeq using "instance transparency", that is, only looking into the definitions of things which are either reducible
or instance
s (and not, for example, the definition of id
)
Reid Barton (Apr 02 2020 at 18:33):
I imagine the answer will be no, otherwise both should succeed (or fail).
Reid Barton (Apr 02 2020 at 18:39):
I see from #print pi.add_comm_monoid
:
add := λ (a a_1 : Π (i : I), f i), id (λ (i : I), add_comm_monoid.add (a i) (a_1 i)),
Kevin Buzzard (Apr 02 2020 at 18:40):
-- the full semimodule instance example : @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@pi.add_comm_monoid β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid α (@ring.to_semiring α _inst_1))) = @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@add_comm_group.to_add_comm_monoid (β → α) (@pi.add_comm_group β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group α _inst_1))) := rfl -- succeeds -- instance transparency version for the full semimodule instance example : @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@pi.add_comm_monoid β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid α (@ring.to_semiring α _inst_1))) = @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@add_comm_group.to_add_comm_monoid (β → α) (@pi.add_comm_group β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group α _inst_1))) := by tactic.reflexivity tactic.transparency.reducible -- succeeds -- But now let's look at the subterms giving the add_comm_monoid structure example : (@pi.add_comm_monoid β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid α (@ring.to_semiring α _inst_1))) = (@add_comm_group.to_add_comm_monoid (β → α) (@pi.add_comm_group β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group α _inst_1))) := rfl -- succeeds example : (@pi.add_comm_monoid β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid α (@ring.to_semiring α _inst_1))) = (@add_comm_group.to_add_comm_monoid (β → α) (@pi.add_comm_group β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group α _inst_1))) := by tactic.reflexivity tactic.transparency.reducible -- fails!
Reid Barton (Apr 02 2020 at 18:40):
oh, actually that field is probably not a problem, since pi.add_comm_group
's add
has id
in the same place.
Reid Barton (Apr 02 2020 at 18:41):
zero := eq.mpr pi.add_comm_group._proof_2 (λ (_x : I), add_comm_group.zero (f _x)),
This certainly looks silly
Kevin Buzzard (Apr 02 2020 at 18:43):
Reid I think my snippet shows that the semimodule terms are defeq even using instance transparency, but they have defeq subterms which are not defeq using instance transparency. I am pretty much at the boundary of my understanding here, I am just posting stuff like by tactic.reflexivity tactic.transparency.reducible
copying from posts Gabriel made earlier this week and am not even 100% sure that I am checking what you are asking.
Reid Barton (Apr 02 2020 at 18:43):
I think you want tactic.transparency.instances
not reducible
Kevin Buzzard (Apr 02 2020 at 18:44):
Eveything succeeds if I change reducible
to instances
Reid Barton (Apr 02 2020 at 18:44):
@Gabriel Ebner help
Kevin Buzzard (Apr 02 2020 at 18:44):
example : (@pi.add_comm_monoid β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid α (@ring.to_semiring α _inst_1))) = (@add_comm_group.to_add_comm_monoid (β → α) (@pi.add_comm_group β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group α _inst_1))) := by tactic.reflexivity tactic.transparency.instances -- works
Gabriel Ebner (Apr 02 2020 at 18:44):
@Reid Barton MWE please.
Kevin Buzzard (Apr 02 2020 at 18:45):
I'll do it
Reid Barton (Apr 02 2020 at 18:48):
I'm really confused about your 2nd and 4th examples. Isn't semimodule
just a constant? How can adding it turn a failing unification problem into one which succeeds?
Reid Barton (Apr 02 2020 at 18:51):
Actually these examples are "easier" than the original failing instance search problem, because here we gave Lean both sides
Reid Barton (Apr 02 2020 at 18:53):
Not to sound like a broken record, but life would be simpler if we didn't allow diamonds and add_comm_group.to_add_comm_monoid
wasn't an instance.
Kevin Buzzard (Apr 02 2020 at 18:55):
import algebra.pi_instances variables (α : Type) [ring α] (β : Type) def ok' : @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@pi.add_comm_monoid β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid α (@ring.to_semiring α _inst_1))) := by apply_instance -- succeeds def not_ok' : @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@add_comm_group.to_add_comm_monoid (β → α) (@pi.add_comm_group β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group α _inst_1))) := sorry --by apply_instance -- fails example : @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@pi.add_comm_monoid β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid α (@ring.to_semiring α _inst_1))) = @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@add_comm_group.to_add_comm_monoid (β → α) (@pi.add_comm_group β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group α _inst_1))) := by tactic.reflexivity tactic.transparency.reducible -- succeeds
Gabriel Ebner (Apr 02 2020 at 18:56):
The second apply_instance
works for me.
Kevin Buzzard (Apr 02 2020 at 18:56):
curses, I might be using some modified mathlib
Kevin Buzzard (Apr 02 2020 at 18:56):
oh yeah, you need to add an #exit
in algebra.pi_instances
:-/
Gabriel Ebner (Apr 02 2020 at 18:57):
Is this meant seriously? Where should I put it?
Kevin Buzzard (Apr 02 2020 at 18:58):
How about
import order.basic import algebra.group.prod import algebra.module import data.finset import ring_theory.subring import tactic.pi_instances namespace pi universes u v w variable {I : Type u} -- The indexing type variable {f : I → Type v} -- The family of types already equipped with instances variables (x y : Π i, f i) (i : I) instance has_zero [∀ i, has_zero $ f i] : has_zero (Π i : I, f i) := ⟨λ i, 0⟩ @[simp] lemma zero_apply [∀ i, has_zero $ f i] : (0 : Π i, f i) i = 0 := rfl instance has_one [∀ i, has_one $ f i] : has_one (Π i : I, f i) := ⟨λ i, 1⟩ @[simp] lemma one_apply [∀ i, has_one $ f i] : (1 : Π i, f i) i = 1 := rfl attribute [to_additive] pi.has_one attribute [to_additive] pi.one_apply instance has_add [∀ i, has_add $ f i] : has_add (Π i : I, f i) := ⟨λ x y, λ i, x i + y i⟩ @[simp] lemma add_apply [∀ i, has_add $ f i] : (x + y) i = x i + y i := rfl instance has_mul [∀ i, has_mul $ f i] : has_mul (Π i : I, f i) := ⟨λ x y, λ i, x i * y i⟩ @[simp] lemma mul_apply [∀ i, has_mul $ f i] : (x * y) i = x i * y i := rfl attribute [to_additive] pi.has_mul attribute [to_additive] pi.mul_apply instance has_inv [∀ i, has_inv $ f i] : has_inv (Π i : I, f i) := ⟨λ x, λ i, (x i)⁻¹⟩ @[simp] lemma inv_apply [∀ i, has_inv $ f i] : x⁻¹ i = (x i)⁻¹ := rfl instance has_neg [∀ i, has_neg $ f i] : has_neg (Π i : I, f i) := ⟨λ x, λ i, -(x i)⟩ @[simp] lemma neg_apply [∀ i, has_neg $ f i] : (-x) i = -x i := rfl attribute [to_additive] pi.has_inv attribute [to_additive] pi.inv_apply instance has_scalar {α : Type*} [∀ i, has_scalar α $ f i] : has_scalar α (Π i : I, f i) := ⟨λ s x, λ i, s • (x i)⟩ @[simp] lemma smul_apply {α : Type*} [∀ i, has_scalar α $ f i] (s : α) : (s • x) i = s • x i := rfl instance semigroup [∀ i, semigroup $ f i] : semigroup (Π i : I, f i) := by pi_instance instance comm_semigroup [∀ i, comm_semigroup $ f i] : comm_semigroup (Π i : I, f i) := by pi_instance instance monoid [∀ i, monoid $ f i] : monoid (Π i : I, f i) := by pi_instance instance comm_monoid [∀ i, comm_monoid $ f i] : comm_monoid (Π i : I, f i) := by pi_instance instance group [∀ i, group $ f i] : group (Π i : I, f i) := by pi_instance instance comm_group [∀ i, comm_group $ f i] : comm_group (Π i : I, f i) := by pi_instance instance add_semigroup [∀ i, add_semigroup $ f i] : add_semigroup (Π i : I, f i) := by pi_instance instance add_comm_semigroup [∀ i, add_comm_semigroup $ f i] : add_comm_semigroup (Π i : I, f i) := by pi_instance instance add_monoid [∀ i, add_monoid $ f i] : add_monoid (Π i : I, f i) := by pi_instance instance add_comm_monoid [∀ i, add_comm_monoid $ f i] : add_comm_monoid (Π i : I, f i) := by pi_instance instance add_group [∀ i, add_group $ f i] : add_group (Π i : I, f i) := by pi_instance instance add_comm_group [∀ i, add_comm_group $ f i] : add_comm_group (Π i : I, f i) := by pi_instance instance semiring [∀ i, semiring $ f i] : semiring (Π i : I, f i) := by pi_instance instance ring [∀ i, ring $ f i] : ring (Π i : I, f i) := by pi_instance instance comm_ring [∀ i, comm_ring $ f i] : comm_ring (Π i : I, f i) := by pi_instance instance mul_action (α) {m : monoid α} [∀ i, mul_action α $ f i] : mul_action α (Π i : I, f i) := { smul := λ c f i, c • f i, mul_smul := λ r s f, funext $ λ i, mul_smul _ _ _, one_smul := λ f, funext $ λ i, one_smul α _ } instance distrib_mul_action (α) {m : monoid α} [∀ i, add_monoid $ f i] [∀ i, distrib_mul_action α $ f i] : distrib_mul_action α (Π i : I, f i) := { smul_zero := λ c, funext $ λ i, smul_zero _, smul_add := λ c f g, funext $ λ i, smul_add _ _ _, ..pi.mul_action _ } instance semimodule (α) {r : semiring α} [∀ i, add_comm_monoid $ f i] [∀ i, semimodule α $ f i] : semimodule α (Π i : I, f i) := { add_smul := λ c f g, funext $ λ i, add_smul _ _ _, zero_smul := λ f, funext $ λ i, zero_smul α _, ..pi.distrib_mul_action _ } variables (α : Type) [ring α] (β : Type) def ok' : @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@pi.add_comm_monoid β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid α (@ring.to_semiring α _inst_1))) := by apply_instance -- succeeds def not_ok' : @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@add_comm_group.to_add_comm_monoid (β → α) (@pi.add_comm_group β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group α _inst_1))) := sorry --by apply_instance -- fails example : @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@pi.add_comm_monoid β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid α (@ring.to_semiring α _inst_1))) = @semimodule α (β → α) (@ring.to_semiring α _inst_1) (@add_comm_group.to_add_comm_monoid (β → α) (@pi.add_comm_group β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group α _inst_1))) := by tactic.reflexivity tactic.transparency.reducible -- succeeds
Kevin Buzzard (Apr 02 2020 at 18:58):
That is like the first third of algebra.pi_instances
Gabriel Ebner (Apr 02 2020 at 19:00):
Ok, fails for me now.
Kevin Buzzard (Apr 02 2020 at 19:01):
I stop just before the line
instance module (α) {r : ring α} [∀ i, add_comm_group $ f i] [∀ i, module α $ f i] : module α (Π i : I, f i) := {..pi.semimodule I f α}
Sebastien Gouezel (Apr 02 2020 at 19:01):
In the first message of the thread, I said to put it just before the module instance.
Kevin Buzzard (Apr 02 2020 at 19:02):
The reason I stop here is that Sebastien is trying to remove module
from this part of the story and use only semimodule
.
Gabriel Ebner (Apr 02 2020 at 20:30):
Why is there an eq.mpr
in the 0 in pi.add_comm_monoid
?
@[instance] protected def pi.add_comm_monoid : Π {I : Type u} {f : I → Type v} [_inst_1 : Π (i : I), add_comm_monoid (f i)], add_comm_monoid (Π (i : I), f i) := λ {I : Type u} {f : I → Type v} [_inst_1 : Π (i : I), add_comm_monoid (f i)], {add := λ (a a_1 : Π (i : I), f i), id (λ (i : I), add_comm_monoid.add (a i) (a_1 i)), add_assoc := _, zero := eq.mpr add_comm_monoid._proof_2 (λ (_x : I), add_comm_monoid.zero (f _x)), zero_add := _, add_zero := _, add_comm := _}
Gabriel Ebner (Apr 02 2020 at 20:37):
With this modifications, the not_ok'
works for me:
instance add_comm_monoid [∀ i, add_comm_monoid $ f i] : add_comm_monoid (Π i : I, f i) := { add := λ a b i, a i + b i, zero := λ i, 0, add_assoc := by simp, add_comm := by intros a b; ext; simp; cc, zero_add := by intro a; ext; simp, add_zero := by intro a; ext; simp, } instance semimodule (α) {r : semiring α} [m : ∀ i, add_comm_monoid $ f i] [∀ i, semimodule α $ f i] : @semimodule α (Π i : I, f i) r (@pi.add_comm_monoid I f m) := { add_smul := λ c f g, funext $ λ i, add_smul _ _ _, zero_smul := λ f, funext $ λ i, zero_smul α _, ..pi.distrib_mul_action _ }
Gabriel Ebner (Apr 02 2020 at 20:39):
Previously, there was an eq.mpr
in the 0 of pi.add_comm_monoid
(this is bad for unification, don't do that). There were also the η-expansions λ i, f i
and λ i, m i
in pi.semimodule
, try to avoid those as well.
Mario Carneiro (Apr 02 2020 at 20:43):
I'm not sure what in pi_instance
is causing that. Would apply
insert id
terms in the goal?
Yury G. Kudryashov (Apr 02 2020 at 22:03):
I tried to understand the magic in by pi_instance
some time ago, and failed.
Yury G. Kudryashov (Apr 02 2020 at 22:04):
Could someone explain me what happens in derive_field
in tactic/pi_instances
(the "else" branch)?
Yury G. Kudryashov (Apr 02 2020 at 22:04):
BTW, the following fails (tried to write this instead of the current instance):
instance add_comm_group [∀ i, add_comm_group $ f i] : add_comm_group (Π i : I, f i) := { .. pi.add_comm_monoid, .. show add_comm_group (Π i, f i), by pi_instance }
Yury G. Kudryashov (Apr 02 2020 at 22:08):
Here is the code of the else
branch:
field ← get_current_field, e ← mk_const field, expl_arity ← get_expl_arity e, xs ← (iota expl_arity).mmap $ λ _, intro1, x ← intro1, applyc field, xs.mmap' (λ h, try $ () <$ (apply (h x) <|> apply h) <|> refine ``(set.image ($ %%x) %%h)) <|> fail "args", return ()
Yury G. Kudryashov (Apr 02 2020 at 22:26):
Rewriting pi
-instances by hand doesn't help.
Yury G. Kudryashov (Apr 02 2020 at 22:27):
I tried the following:
import order.basic import algebra.group.prod import algebra.module import data.finset import ring_theory.subring import tactic.pi_instances namespace pi universes u v w variable {I : Type u} -- The indexing type variable {f : I → Type v} -- The family of types already equipped with instances variables (x y : Π i, f i) (i : I) @[to_additive] instance has_one [∀ i, has_one $ f i] : has_one (Π i : I, f i) := ⟨λ i, 1⟩ @[to_additive, simp] lemma one_apply [∀ i, has_one $ f i] : (1 : Π i, f i) i = 1 := rfl @[to_additive] instance has_mul [∀ i, has_mul $ f i] : has_mul (Π i : I, f i) := ⟨λ x y, λ i, x i * y i⟩ @[simp, to_additive] lemma mul_apply [∀ i, has_mul $ f i] : (x * y) i = x i * y i := rfl @[to_additive] instance has_inv [∀ i, has_inv $ f i] : has_inv (Π i : I, f i) := ⟨λ x, λ i, (x i)⁻¹⟩ @[simp, to_additive] lemma inv_apply [∀ i, has_inv $ f i] : x⁻¹ i = (x i)⁻¹ := rfl instance has_scalar {α : Type*} [∀ i, has_scalar α $ f i] : has_scalar α (Π i : I, f i) := ⟨λ s x, λ i, s • (x i)⟩ @[simp] lemma smul_apply {α : Type*} [∀ i, has_scalar α $ f i] (s : α) : (s • x) i = s • x i := rfl @[to_additive add_semigroup] instance semigroup [∀ i, semigroup $ f i] : semigroup (Π i : I, f i) := by refine_struct { mul := (*) }; tactic.derive_field @[to_additive add_comm_semigroup] instance comm_semigroup [∀ i, comm_semigroup $ f i] : comm_semigroup (Π i : I, f i) := by refine_struct { mul := (*) }; tactic.derive_field @[to_additive add_monoid] instance monoid [∀ i, monoid $ f i] : monoid (Π i : I, f i) := by refine_struct { one := (1 : Π i, f i), mul := (*) }; tactic.derive_field @[to_additive add_comm_monoid] instance comm_monoid [∀ i, comm_monoid $ f i] : comm_monoid (Π i : I, f i) := by refine_struct { one := (1 : Π i, f i), mul := (*) }; tactic.derive_field @[to_additive add_group] instance group [∀ i, group $ f i] : group (Π i : I, f i) := by refine_struct { inv := has_inv.inv, one := (1 : Π i, f i), mul := (*) }; tactic.derive_field @[to_additive add_comm_group] instance comm_group [∀ i, comm_group $ f i] : comm_group (Π i : I, f i) := by refine_struct { inv := has_inv.inv, one := (1 : Π i, f i), mul := (*) }; tactic.derive_field instance semiring [∀ i, semiring $ f i] : semiring (Π i : I, f i) := by refine_struct { zero := (0 : Π i, f i), one := 1, add := (+), mul := (*) }; tactic.derive_field instance ring [∀ i, ring $ f i] : ring (Π i : I, f i) := by refine_struct { zero := (0 : Π i, f i), one := 1, add := (+), mul := (*), neg := has_neg.neg }; tactic.derive_field instance comm_ring [∀ i, comm_ring $ f i] : comm_ring (Π i : I, f i) := by refine_struct { zero := (0 : Π i, f i), one := 1, add := (+), mul := (*), neg := has_neg.neg }; tactic.derive_field instance mul_action (α) {m : monoid α} [∀ i, mul_action α $ f i] : mul_action α (Π i : I, f i) := { smul := λ c f i, c • f i, mul_smul := λ r s f, funext $ λ i, mul_smul _ _ _, one_smul := λ f, funext $ λ i, one_smul α _ } instance distrib_mul_action (α) {m : monoid α} [∀ i, add_monoid $ f i] [∀ i, distrib_mul_action α $ f i] : distrib_mul_action α (Π i : I, f i) := { smul_zero := λ c, funext $ λ i, smul_zero _, smul_add := λ c f g, funext $ λ i, smul_add _ _ _, ..pi.mul_action _ } variables (I f) instance semimodule (α) {r : semiring α} [∀ i, add_comm_monoid $ f i] [∀ i, semimodule α $ f i] : semimodule α (Π i : I, f i) := { add_smul := λ c f g, funext $ λ i, add_smul _ _ _, zero_smul := λ f, funext $ λ i, zero_smul α _, ..pi.distrib_mul_action _ } variables {I f} lemma ok (α : Type) [ring α] (β : Type) : @semimodule.{0 0} α (β → α) (@ring.to_semiring.{0} α _inst_1) (@pi.add_comm_monoid.{0 0} β (λ (a : β), α) (λ (i : β), @semiring.to_add_comm_monoid.{0} α (@ring.to_semiring.{0} α _inst_1))) := by apply_instance -- ok lemma not_ok (α : Type) [ring α] (β : Type) : @semimodule.{0 0} α (β → α) (@ring.to_semiring.{0} α _inst_1) (@add_comm_group.to_add_comm_monoid.{0} (β → α) (@pi.add_comm_group.{0 0} β (λ (a : β), α) (λ (i : β), @ring.to_add_comm_group.{0} α _inst_1))) := by apply_instance -- fails end pi
Yury G. Kudryashov (Apr 02 2020 at 22:29):
And I don't see the difference between my instances and @Gabriel Ebner 's
Yury G. Kudryashov (Apr 02 2020 at 22:42):
Ah, I didn't apply his patch to semimodule
.
Scott Morrison (Apr 02 2020 at 22:57):
Regarding this eq.mpr
---- I put a lot of effort in the transport
tactic to ensure none of these turned up in transport
ed structures.
Scott Morrison (Apr 02 2020 at 22:58):
I haven't looked at this case, but we should ping @Simon Hudon to ask about pi_instances
, I think.
Scott Morrison (Apr 02 2020 at 22:58):
When writing transport
, I found that often the eq.mpr
s (and even theeq.rec
s) that were being produced were actually trivial:
Scott Morrison (Apr 02 2020 at 23:00):
when you looked at the equality you were running eq.mpr
along, it was in fact refl
, and eq_mpr_refl
(only recently inmaster
) would reduce it.
Scott Morrison (Apr 02 2020 at 23:01):
Sometimes it was very hard to avoid the eq.mpr
being generated, and you had to intercept it after the fact with the simp_result
tactic introduced in #2251.
Simon Hudon (Apr 02 2020 at 23:03):
I haven't spent much time looking at eq.mp
. @Scott Morrison I think you know more than me on the topic
Yury G. Kudryashov (Apr 02 2020 at 23:11):
How do I add debug output to a tactic script? Do we have something more convenient than trace
?
Yury G. Kudryashov (Apr 02 2020 at 23:11):
I just want to prepend string labels to output strings.
Simon Hudon (Apr 02 2020 at 23:41):
You could write your own that does:
meta def my_trace (msg : string) : tactic unit := trace ("prefix: " ++ msg)
Scott Morrison (Apr 02 2020 at 23:45):
Ok, so wrapping pi_instance
in simp_result
does make the eq.mpr
in #print pi.add_comm_monoid
go away.
Scott Morrison (Apr 02 2020 at 23:50):
So I think this counts as a fix to @Sebastien Gouezel's original problem?
Scott Morrison (Apr 02 2020 at 23:50):
How should we implement it?
Scott Morrison (Apr 02 2020 at 23:50):
I can make a PR that just patches pi_instances
to use simp_result
. (Presumably I will play with squeeze_simp
to identify exactly which simp lemmas simp_result
actually needs.
Scott Morrison (Apr 02 2020 at 23:51):
And then @Sebastien Gouezel can continue his branch on top of that?
Yury G. Kudryashov (Apr 02 2020 at 23:54):
Sounds like a good idea.
Scott Morrison (Apr 02 2020 at 23:55):
We will need to remember to make @Gabriel Ebner's patch to the semimodule
instance in pi_instances
, but I think this can be done in @Sebastien Gouezel's branch, as this is a separate issue from the automation problem.
Reid Barton (Apr 03 2020 at 00:01):
This seems super hacky. Couldn't we just build the correct term in the first place using structural recursion on the shape of the operation we want to define?
Yury G. Kudryashov (Apr 03 2020 at 00:06):
Can you do this? It seems that either applyc
or apply
generates this eq.mpr
.
Yury G. Kudryashov (Apr 03 2020 at 00:16):
No, for one
somehow intro1
closes the goal.
Yury G. Kudryashov (Apr 03 2020 at 00:16):
And generates eq.mpr
.
Yury G. Kudryashov (Apr 03 2020 at 00:19):
intro_core
does the same.
Yury G. Kudryashov (Apr 03 2020 at 00:20):
How do I print debug current proof state (i.e., what Lean normally prints in "goals" in the tactic proof mode)?
Scott Morrison (Apr 03 2020 at 00:21):
Dumb eq.mpr
s seem hard to avoid. :-(
Scott Morrison (Apr 03 2020 at 00:21):
But maybe we can go into the C++?
Yury G. Kudryashov (Apr 03 2020 at 00:29):
I found out the difference between 1
and mul
.
Yury G. Kudryashov (Apr 03 2020 at 00:30):
For mul
, expl_arity = 2
, and after two intro1
, the goal is Π i, f i
Yury G. Kudryashov (Apr 03 2020 at 00:30):
For one
, expl_arity = 1
, and after one intro1
, the goal is f i
.
Reid Barton (Apr 03 2020 at 00:31):
What I had in mind was to just build the required expr
directly from the constructors.
Yury G. Kudryashov (Apr 03 2020 at 00:32):
My tactic programming skills are, let's say, far from being good.
Reid Barton (Apr 03 2020 at 00:32):
exactly, don't use tactics!
Yury G. Kudryashov (Apr 03 2020 at 00:33):
What do you propose?
Yury G. Kudryashov (Apr 03 2020 at 00:33):
Should we have pi_instance
tactic or not?
Yury G. Kudryashov (Apr 03 2020 at 00:35):
Of course, we can leave only Prop
branch, and use := by refine_struct { one := (1 : Π i, f i) }; pi_props
Yury G. Kudryashov (Apr 03 2020 at 00:35):
This will give us all the required defeq's.
Yury G. Kudryashov (Apr 03 2020 at 00:36):
But if we want to generate all fields automatically, then we need some meta code analyzing the target.
Yury G. Kudryashov (Apr 03 2020 at 00:36):
The current code works well for unary and binary operations but fails for constants.
Scott Morrison (Apr 03 2020 at 00:38):
The
xs.mmap' (λ h, try $ () <$ (apply (h x) <|> apply h) <|> refine ``(set.image ($ %%x) %%h)) <|> fail "args",
block deserves some documentation. :-)
Scott Morrison (Apr 03 2020 at 00:39):
(but I think is unrelated to what we're seeing at the moment)
Yury G. Kudryashov (Apr 03 2020 at 00:41):
@Scott Morrison I added some trace
s, and the goal one
is closed by x ← intro1
Scott Morrison (Apr 03 2020 at 00:42):
how does the rest of the tactic not fail, then?
Scott Morrison (Apr 03 2020 at 00:42):
doesn't applyc
choke when there's no goal?
Scott Morrison (Apr 03 2020 at 00:42):
I'm confused
Yury G. Kudryashov (Apr 03 2020 at 00:43):
It just exits. At least trace
doesn't print anything.
Reid Barton (Apr 03 2020 at 00:45):
It seems easy to get the target type and appropriate bits of the context and compute by a pure function the term which we should produce.
Reid Barton (Apr 03 2020 at 00:45):
I can't try right now because I need to prepare a seminar for tomorrow
Yury G. Kudryashov (Apr 03 2020 at 00:46):
I don't understand how this manages to define pi.monoid
:
namespace tactic open interactive interactive.types lean.parser expr open functor has_seq list nat tactic.interactive set_option pp.all true meta def derive_field' : tactic unit := do b ← target >>= is_prop, field ← get_current_field, if b then do field ← get_current_field, vs ← introv [] <|> pure [], hs ← intros <|> pure [], resetI, x ← get_unused_name, try (() <$ ext1 [rcases_patt.one x] <|> () <$ intro x), x' ← try_core (get_local x), applyc field, hs.mmap (λ h, try $ () <$ (to_expr ``(congr_fun %%h %%(x'.iget)) >>= apply) <|> () <$ apply (h x'.iget) <|> () <$ (to_expr ``(set.mem_image_of_mem _ %%h) >>= apply) <|> () <$ (solve_by_elim) ), return () else focus1 $ do field ← get_current_field, e ← mk_const field, expl_arity ← get_expl_arity e, xs ← (iota expl_arity).mmap $ λ _, intro1, t ← target >>= whnf, if expr.is_pi t then do trace field, x ← intro1, applyc field, xs.mmap' (λ h, apply (h x) <|> apply h), return () else return () /-- `pi_instance` constructs an instance of `my_class (Π i : I, f i)` where we know `Π i, my_class (f i)`. If an order relation is required, it defaults to `pi.partial_order`. Any field of the instance that `pi_instance` cannot construct is left untouched and generated as a new goal. -/ meta def pi_instance' : tactic unit := refine_struct ``( { ..pi.partial_order, .. } ); propagate_tags (try (derive_field' ; done)) run_cmd add_interactive [`pi_instance'] end tactic
Yury G. Kudryashov (Apr 03 2020 at 00:47):
(I put this at the top of algebra/pi_instances.lean
to avoid compiling other files)
Yury G. Kudryashov (Apr 03 2020 at 00:48):
@Reid Barton May I open an issue and assign it to you?
Scott Morrison (Apr 03 2020 at 00:48):
oh ....
Scott Morrison (Apr 03 2020 at 00:49):
I think what is going on is that derive_field
is actually _failing_ on one
.
Scott Morrison (Apr 03 2020 at 00:49):
Which by itself is fine, it's wrapped in try
.
Scott Morrison (Apr 03 2020 at 00:49):
However we then plough on trying to derive_field
the axioms as well.
Scott Morrison (Apr 03 2020 at 00:49):
And somehow one
is solved by unification at a later step.
Scott Morrison (Apr 03 2020 at 00:50):
But we mess up and introduce the bad answer there?
Yury G. Kudryashov (Apr 03 2020 at 00:50):
Indeed!
Yury G. Kudryashov (Apr 03 2020 at 00:50):
Removed try
, and now it fails.
Scott Morrison (Apr 03 2020 at 00:52):
Yes, look at this, from trace_state
at the beginning of solving zero_add
:
(@has_zero.zero.{(max u v)} (Π (i : I), f i) (@has_zero.mk.{(max u v)} (Π (i : I), f i) (@eq.mpr.{(max (u+1) (v+1))} (Π (i : I), f i) (Π (i : I), f i) (@id.{0} (@eq.{(max (u+1) (v+1))+1} (Type (max u v)) (Π (i : I), f i) (Π (i : I), f i)) (@eq.refl.{(max (u+1) (v+1))+1} (Type (max u v)) (Π (i : I), f i))) ?m_1))) a)
We've just got a metavariable for zero
!
Scott Morrison (Apr 03 2020 at 00:52):
Until you turn pp.all
on, you can't even see this. It just happily prints as 0
...
Scott Morrison (Apr 03 2020 at 00:53):
Kind of scary that this was working at all. :-)
Scott Morrison (Apr 03 2020 at 00:54):
Haha...
Scott Morrison (Apr 03 2020 at 00:54):
it turns out we've already fixed this issue
Scott Morrison (Apr 03 2020 at 00:55):
https://github.com/leanprover-community/lean/pull/169
Scott Morrison (Apr 03 2020 at 00:55):
This is the reason why we're seeing arity = 1 on zero
and one
.
Scott Morrison (Apr 03 2020 at 00:56):
I suspect that when this gets merged, and we move to Lean 3.8, derive_field
will no longer fail, the eq.mpr
will disappear, and we'll all be happy.
Yury G. Kudryashov (Apr 03 2020 at 00:57):
I don't think so. The target is Π i, f i
, so it actually has arity = 1
Scott Morrison (Apr 03 2020 at 00:58):
oh, hrm.
Yury G. Kudryashov (Apr 03 2020 at 01:00):
BTW, now I'm testing on has_zero
instead of monoid
.
Scott Morrison (Apr 03 2020 at 01:00):
I don't think that's what expl_arity
is meant to mean. Notice that it is 2 for add
, and 1 for zero
.
Scott Morrison (Apr 03 2020 at 01:00):
It should be 2 for add
, and 0 for zero
.
Scott Morrison (Apr 03 2020 at 01:01):
With has_zero
we'll just see it fail at the x <- intro1
line.
Yury G. Kudryashov (Apr 03 2020 at 01:02):
This works for has_zero
but gives eq.mpr
in the result:
namespace tactic open interactive interactive.types lean.parser expr open functor has_seq list nat tactic.interactive meta def derive_field' : tactic unit := do b ← target >>= is_prop, field ← get_current_field, if b then do field ← get_current_field, vs ← introv [] <|> pure [], hs ← intros <|> pure [], resetI, x ← get_unused_name, try (() <$ ext1 [rcases_patt.one x] <|> () <$ intro x), x' ← try_core (get_local x), applyc field, hs.mmap (λ h, try $ () <$ (to_expr ``(congr_fun %%h %%(x'.iget)) >>= apply) <|> () <$ apply (h x'.iget) <|> () <$ (to_expr ``(set.mem_image_of_mem _ %%h) >>= apply) <|> () <$ (solve_by_elim) ), return () else focus1 $ do field ← get_current_field, e ← mk_const field, expl_arity ← get_expl_arity e, xs ← (iota expl_arity).mmap $ λ _, intro1, t ← target >>= whnf, if expr.is_pi t then do x ← intro1, applyc field, xs.mmap' (λ h, apply (h x) <|> apply h), return () else applyc field, return () /-- `pi_instance` constructs an instance of `my_class (Π i : I, f i)` where we know `Π i, my_class (f i)`. If an order relation is required, it defaults to `pi.partial_order`. Any field of the instance that `pi_instance` cannot construct is left untouched and generated as a new goal. -/ meta def pi_instance' : tactic unit := refine_struct ``( { ..pi.partial_order, .. } ); propagate_tags (derive_field' ; done) run_cmd add_interactive [`pi_instance'] end tactic
Scott Morrison (Apr 03 2020 at 01:04):
I see. You're saying that the applyc
call introduces an eq.mpr
all by itself?
Scott Morrison (Apr 03 2020 at 01:04):
Can we reproduce that in a standalone tactic block, independent of using pi_instance
?
Yury G. Kudryashov (Apr 03 2020 at 01:10):
How do I try to close the goal using apply e
, and do something else if it fails?
Yury G. Kudryashov (Apr 03 2020 at 01:10):
I mean, inside do
block
Scott Morrison (Apr 03 2020 at 01:12):
<|>
Scott Morrison (Apr 03 2020 at 01:12):
I'm remaining pretty sure the problem is the expl_arity
being reported for one
is wrong.
Scott Morrison (Apr 03 2020 at 01:12):
Your last update to derive_field
doesn't make sense.
Scott Morrison (Apr 03 2020 at 01:13):
The else
branch of
if expr.is_pi t then do x ← intro1, applyc field, xs.mmap' (λ h, apply (h x) <|> apply h), return () else applyc field, return ()
really needs to have a x ← intro1
to consume the argument of the Pi type
Scott Morrison (Apr 03 2020 at 01:13):
Your code is working without it because expl_arity
is wrong for zero
/one
because of the explicit argument in has_zero
/has_one
that is fixed in that PR I linked above.
Yury G. Kudryashov (Apr 03 2020 at 01:14):
Try to add trace target
before expl_arity
.
Yury G. Kudryashov (Apr 03 2020 at 01:16):
Sorry, you're right.
Yury G. Kudryashov (Apr 03 2020 at 01:16):
e
is has_one.one
.
Scott Morrison (Apr 03 2020 at 01:17):
Actually, while I remain convinced expl_arity
is being calculated incorrectly, I no longer believe it's because of this implicit/explict argument inhas_one
.
Scott Morrison (Apr 03 2020 at 01:18):
The target before expl_arity
in has_add
is
(Π (i : I), f i) → (Π (i : I), f i) → Π (i : I), f i
and we get told 2
Scott Morrison (Apr 03 2020 at 01:18):
The target before expl_arity
in has_zero
is
Π (i : I), f i
and we get told 1
Scott Morrison (Apr 03 2020 at 01:18):
These are not consistent.
Scott Morrison (Apr 03 2020 at 01:19):
I think in the has_add
case it should be 3
?
Scott Morrison (Apr 03 2020 at 01:20):
oh, no, it is the PR I was talking about
Yury G. Kudryashov (Apr 03 2020 at 01:20):
That's why we have x
and xs
.
P.S.: Please use syntax highlighting.
Scott Morrison (Apr 03 2020 at 01:21):
it's not the target
that matters, it is e
that matters.
Scott Morrison (Apr 03 2020 at 01:21):
and the expl_arity
of add_comm_monoid.add.{?l_1}
is (correctly) 2
Yury G. Kudryashov (Apr 03 2020 at 01:21):
This defines pi.has_one
but still produces eq.mpr
:
meta def derive_field' : tactic unit := do b ← target >>= is_prop, field ← get_current_field, if b then do field ← get_current_field, vs ← introv [] <|> pure [], hs ← intros <|> pure [], resetI, x ← get_unused_name, try (() <$ ext1 [rcases_patt.one x] <|> () <$ intro x), x' ← try_core (get_local x), applyc field, hs.mmap (λ h, try $ () <$ (to_expr ``(congr_fun %%h %%(x'.iget)) >>= apply) <|> () <$ apply (h x'.iget) <|> () <$ (to_expr ``(set.mem_image_of_mem _ %%h) >>= apply) <|> () <$ (solve_by_elim) ), return () else focus1 $ do field ← get_current_field, e ← mk_const field, expl_arity ← if field = `has_one.one then return 0 else get_expl_arity e, xs ← (iota expl_arity).mmap $ λ _, intro1, x ← intro_core `_, applyc field, xs.mmap' (λ h, try $ () <$ (apply (h x) <|> apply h) <|> refine ``(set.image ($ %%x) %%h)) <|> fail "args", return ()
Scott Morrison (Apr 03 2020 at 01:21):
and the expl_arity
of has_zero.zero.{?l_1}
is (suprisingly) 1
Scott Morrison (Apr 03 2020 at 01:22):
Interesting, let me try that
Scott Morrison (Apr 03 2020 at 01:25):
Okay, I agree that is sensible code, and still produces an eq.mpr
.
Yury G. Kudryashov (Apr 03 2020 at 01:26):
Here is an MWE:
instance has_zero [∀ i, has_zero $ f i] : has_zero (Π i : I, f i) := by refine_struct { .. }; intros; apply has_zero.zero #print pi.has_zero
Yury G. Kudryashov (Apr 03 2020 at 01:28):
So it is a bug in refine_struct
.
Yury G. Kudryashov (Apr 03 2020 at 01:28):
It works with refine
.
Scott Morrison (Apr 03 2020 at 01:29):
And a completely standalone example:
import tactic.interactive namespace pi universes u v variable {I : Type u} -- The indexing type variable {f : I → Type v} -- The family of types already equipped with instances instance has_zero [∀ i, has_zero $ f i] : has_zero (Π i : I, f i) := by refine_struct { .. }; propagate_tags (do tactic.intro1, tactic.applyc `has_zero.zero) set_option pp.all true #print pi.has_zero end pi
Yury G. Kudryashov (Apr 03 2020 at 01:30):
refine_struct
produces an eq.mpr
, refine
doesn't.
Yury G. Kudryashov (Apr 03 2020 at 01:31):
And refine_struct
is too long for me to understand and catch the bug.
Scott Morrison (Apr 03 2020 at 01:33):
Yeah, I'm looking through it now. Hopefully @Simon Hudon will stop by sometime. :-)
Scott Morrison (Apr 03 2020 at 01:36):
I'm worried it falls all the way down to meta constant pexpr.mk_structure_instance : structure_instance_info → pexpr
.
Scott Morrison (Apr 03 2020 at 01:38):
no, that makes no sense, it is just producing something with metavariables: @has_zero.mk.{?l_1} ?m_2 ?m_3
Scott Morrison (Apr 03 2020 at 01:41):
In
import tactic.interactive namespace pi universes u v variable {I : Type u} -- The indexing type variable {f : I → Type v} -- The family of types already equipped with instances set_option pp.all true open tactic instance has_zero [∀ i, has_zero $ f i] : has_zero (Π i : I, f i) := begin refine_struct { .. }, result >>= trace, (do tactic.intro1, tactic.applyc `has_zero.zero) end #print pi.has_zero end pi
You can clearly see that the problem has already occurred by the time we leave refine_struct
: the eq.mpr
is there no matter how we discharge the goal later.
Yury G. Kudryashov (Apr 03 2020 at 01:41):
Even shorter:
instance has_zero [∀ i, has_zero $ f i] : has_zero (Π i : I, f i) := by refine_struct { .. }; exact λ _, 0
Scott Morrison (Apr 03 2020 at 01:45):
You can wrap the whole refine_struct { .. }; exact λ _, 0
with simp_result
, and successfully clear the eq.mpr
, but it's not possible to fix refine_struct
this way: until the metavariables have been filled in simp_result
doesn't work.
Scott Morrison (Apr 03 2020 at 01:50):
Ok, the eq.mpr
in the result
is introduced in the line
gs ← with_enable_tags ( mzip_with (λ (n : name × name) v, do set_goals [v], try (interactive.unfold (provided.map $ λ ⟨s,f⟩, f.update_prefix s) (loc.ns [none])), apply_auto_param <|> apply_opt_param <|> (set_main_tag [`_field,n.2,n.1]), get_goals) missing_f' vs),
in meta def refine_one
in tactic/interactive.lean
.
Scott Morrison (Apr 03 2020 at 01:51):
(You can trace_result
before and after.)
Scott Morrison (Apr 03 2020 at 01:54):
And if we comment out the line
try (interactive.unfold (provided.map $ λ ⟨s,f⟩, f.update_prefix s) (loc.ns [none])),
then the problem goes away.
Yury G. Kudryashov (Apr 03 2020 at 01:55):
Why do we need this line?
Yury G. Kudryashov (Apr 03 2020 at 01:59):
When I read the code, I wish we had much more comments. Why do I forget about this when I write the code?
Scott Morrison (Apr 03 2020 at 01:59):
I have no idea. :-)
Scott Morrison (Apr 03 2020 at 01:59):
:-)
Scott Morrison (Apr 03 2020 at 01:59):
I will go and add extra comments to all my current PRs. I feel exactly the same way at the moment.
Scott Morrison (Apr 03 2020 at 02:00):
In fact I'm starting to think this is a bug in core again.
Scott Morrison (Apr 03 2020 at 02:01):
This interactive.unfold
should be failing, on the basis that it's not really changing anything. Except, of course, that it is changing something: introducing this pesky eq.mpr
!
Scott Morrison (Apr 03 2020 at 02:01):
It seems we are calling interactive.unfold [] (loc.ns [none])
here, which means simp_core
is being called with no lemmas at all.
Scott Morrison (Apr 03 2020 at 02:01):
So it has no excuse for doing anything.
Yury G. Kudryashov (Apr 03 2020 at 02:02):
Can you try to turn it into an MWE?
Yury G. Kudryashov (Apr 03 2020 at 02:03):
I mean, an example without refine_struct
.
Scott Morrison (Apr 03 2020 at 02:03):
Trying... if we just run
import tactic.interactive namespace pi universes u v variable {I : Type u} -- The indexing type variable {f : I → Type v} -- The family of types already equipped with instances set_option pp.all true open tactic instance has_zero [∀ i, has_zero $ f i] : has_zero (Π i : I, f i) := begin refine { .. }, tactic.interactive.unfold [] (interactive.loc.ns [none]), result >>= trace, exact (λ_, 0), end #print pi.has_zero end pi
Scott Morrison (Apr 03 2020 at 02:04):
Then unfold
correctly fails, saying it has nothing to do.
Scott Morrison (Apr 03 2020 at 02:06):
But somehow inside refine_struct
, with apparently the same goal, it succeeds and introduces the eq.mpr
.
Mario Carneiro (Apr 03 2020 at 02:07):
simp
does stuff even with no lemmas
Mario Carneiro (Apr 03 2020 at 02:07):
like unfolding beta redexes
Scott Morrison (Apr 03 2020 at 02:16):
and apparently introducing eq.mpr
along eq.refl _
, just for the fun of it. :-(
Scott Morrison (Apr 03 2020 at 02:18):
We should call it unsimp
here. You need to hit the result with simp [eq_mpr_refl]
to get something reasonable back...
Scott Morrison (Apr 03 2020 at 02:20):
@Simon Hudon, when you notice this thread, don't feel the need to read from the top. We can write a good summary now, we're getting much closer to the source.
Yury G. Kudryashov (Apr 03 2020 at 02:23):
Usually people call simp
when the goal is in Prop
, so the exact proof term is not important.
Scott Morrison (Apr 03 2020 at 02:24):
I just tried commenting out that offending line inside refine_struct
. All the tests in test/examples.lean
succeed, but maybe they don't test enough.
Yury G. Kudryashov (Apr 03 2020 at 02:25):
I think that the logic of refine_struct
should be changed so that if there is no field with a given name among explicitly provided fields, then it doesn't unfold
/simp
/whatever.
Simon Hudon (Apr 03 2020 at 02:28):
What is the status here?
Yury G. Kudryashov (Apr 03 2020 at 02:30):
The following code generates an eq.mpr
in the result:
import tactic.interactive instance has_zero [∀ i, has_zero $ f i] : has_zero (Π i : I, f i) := by refine_struct { .. }; exact λ _, 0
Yury G. Kudryashov (Apr 03 2020 at 02:30):
@Scott Morrison found the offending line in refine_struct
Yury G. Kudryashov (Apr 03 2020 at 02:31):
It's
try (interactive.unfold (provided.map $ λ ⟨s,f⟩, f.update_prefix s) (loc.ns [none])),
Yury G. Kudryashov (Apr 03 2020 at 02:32):
That's all I know. May be @Scott Morrison has more details to add
Yury G. Kudryashov (Apr 03 2020 at 02:33):
I wonder why refine_struct
does something to a goal if there is no field with the same name provided in the { .. }
block.
Yury G. Kudryashov (Apr 03 2020 at 02:33):
And how hard would it be to fix this.
Simon Hudon (Apr 03 2020 at 02:33):
If we use dunfold
instead of unfold
, does that solve the problem?
Yury G. Kudryashov (Apr 03 2020 at 02:36):
No eq.mpr
but there is id
.
Yury G. Kudryashov (Apr 03 2020 at 02:36):
Don't know how bad is it for class instances.
Simon Hudon (Apr 03 2020 at 02:38):
I think it's going to be an issue too. Can you skip that line if the goal is not a prop?
Yury G. Kudryashov (Apr 03 2020 at 02:39):
I don't understand what happens in refine_one
.
Yury G. Kudryashov (Apr 03 2020 at 02:41):
If you insert this after the definition of refine_struct
in tactic.interactive
, it will tell you whether the problem is fixed or not:
instance pi.has_one {α : Type*} {β : α → Type*} [Π x, has_one (β x)] : has_one (Π x, β x) := by refine_struct { .. }; exact λ _, 1 set_option pp.all true #print pi.has_one
Simon Hudon (Apr 03 2020 at 02:41):
refine_struct
has to work recursively because structures can extend each other and I have to elaborate one instance at a time
Simon Hudon (Apr 03 2020 at 02:42):
refine_one
takes the definition of a struct and an instance, infers the missing fields, tries to fill them up and tag the left-over goals
Simon Hudon (Apr 03 2020 at 02:43):
Ok, I think I see how we can do things.
Simon Hudon (Apr 03 2020 at 02:45):
We can create a copy of the current goal, rewrite it using dunfold
, get the new statement with target
and use it with change
in the original goal
Yury G. Kudryashov (Apr 03 2020 at 02:48):
What are you doing to fields that are not in provided
?
Simon Hudon (Apr 03 2020 at 02:49):
Creating a meta variable with their type and add it to the list of goals
Yury G. Kudryashov (Apr 03 2020 at 03:00):
Why do you run unfold
on them?
Simon Hudon (Apr 03 2020 at 03:03):
I think it's because otherwise I can get this kind of expression { add := my_add }.add x y ≤ _
Yury G. Kudryashov (Apr 03 2020 at 03:04):
I see, they can depend on provided fields.
Yury G. Kudryashov (Apr 03 2020 at 03:04):
(and Prop
s normally do)
Simon Hudon (Apr 03 2020 at 03:05):
Yes and I'd rather have my_add x y ≤ _
Simon Hudon (Apr 03 2020 at 03:10):
I'm heading to bed. Good luck with refine_struct
Scott Morrison (Apr 03 2020 at 03:20):
We may ask for help again tomorrow. :-)
Yury G. Kudryashov (Apr 03 2020 at 05:08):
Let's see if #2319 works.
Last updated: Dec 20 2023 at 11:08 UTC