Zulip Chat Archive

Stream: general

Topic: Why is `coe_fn_coe_base` a `simp` lemma?


Anne Baanen (Mar 05 2021 at 11:19):

The library note function coercion points out that docs#coe_fn_coe_base is likely to cause simp loops, if you have a @[simp] lemma foo_hom.coe_coe (f : foo_hom A B) : ((f : bar_hom A B) : A → B) = (f : A → B). As a consequence, each foo_hom has to declare its own has_coe_to_fn instance.

I can't think of any types where we really want to have the double coercion as the simp normal form, so why is coe_fn_coe_base actually a simp lemma at all?

Anne Baanen (Mar 05 2021 at 11:29):

(I'm currently rebuilding mathlib with the simp attribute removed, no errors yet...)

Gabriel Ebner (Mar 05 2021 at 11:32):

EDIT: sorry wrong lemma

Gabriel Ebner (Mar 05 2021 at 11:33):

For everybody else who got confused, Anne wants to remove the ⇑x = ⇑↑x lemma.

Eric Wieser (Mar 05 2021 at 11:34):

Isn't the problem that the on the LHS and RHS are different operations, and that the simp lemmas about say ring_hom will only be about the one that's defeq to ring_hom.has_coe_to_fun?

Anne Baanen (Mar 05 2021 at 11:39):

nevermind, sorry

Anne Baanen (Mar 05 2021 at 11:42):

If coe_fn_coe_base is actually used anywhere, that issue would be solved, but we'd have to deal with ⇑↑↑↑↑(f : alg_hom A B) as a simp-normal form. (Since we can go alg_hom -> ring_hom -> monoid_with_zero_hom -> monoid_hom -> mul_hom.)

Anne Baanen (Mar 05 2021 at 11:46):

I found a counterexample where coe_fn_coe_base is needed!

mathlib/src/topology/algebra/module.lean:1369:14: error: solve1 tactic failed, focused goal has not been solved
state:
R : Type u_1,
M : Type u_2,
M₂ : Type u_3,
_inst_1 : topological_space M,
_inst_2 : topological_space M₂,
_inst_3 : ring R,
_inst_4 : add_comm_group M,
_inst_5 : topological_add_group M,
_inst_6 : module R M,
_inst_7 : add_comm_group M₂,
_inst_8 : module R M₂,
e : M L[R] M₂,
f : M L[R] M₂,
F : units (M L[R] M),
hF : F = (e.symm).comp f,
x : M
 e (F x) = f x

Anne Baanen (Mar 05 2021 at 12:24):

... and another instance of coe_fn (f : units _) in mathlib/src/analysis/calculus/fderiv.lean:2128

Eric Wieser (Mar 05 2021 at 12:26):

If ⇑↑↑↑↑f were the simp-normal form, then presumably we would no longer need ring_hom.map_mul as a simp lemma because ⇑↑(↑↑↑f) (x * y) would match mul_hom.map_mul (↑↑↑f) x y?

Anne Baanen (Mar 05 2021 at 12:28):

I think so, although it is quite probable it will then fail to match ⇑↑(↑↑↑f) 1with one_hom.map_one.

Eric Wieser (Mar 05 2021 at 12:29):

Oh good point, diamonds make that not a good idea at all

Eric Wieser (Mar 05 2021 at 12:29):

In addition to the pile of arrows already being not a good idea at all

Anne Baanen (Mar 05 2021 at 12:36):

Compilation finished. In summary I see:

  • 2 simps that require adding coe_fn_coe_base
  • 2 existing simp only [coe_fn_coe_base, ...]
  • 2 existing rw ← coe_fn_coe_base
  • 4 existing simp [-coe_fn_coe_base, ...]

Eric Wieser (Mar 05 2021 at 12:37):

Did you test what happens if you also remove the has_coe_to_fun instances?

Anne Baanen (Mar 05 2021 at 12:38):

Not yet

Anne Baanen (Mar 05 2021 at 12:42):

One example, in algebra/group/hom.lean is monoid_hom.comp: this fails (times out) if coe_fn_coe_base is @[simp] and there is no has_coe_to_fun instance for monoid_hom. Removing the simp attribute as well, makes it work again.

Anne Baanen (Mar 05 2021 at 12:46):

Because of diamonds we indeed have that only one of one_hom.map_one or mul_hom.map_mul gets matched with monoid_hom:

set_option trace.simplify.rewrite true

example [monoid M] [monoid N]
  (f : M →* N) (a b : M) : f (a * b) = f a * f b := by simp only [mul_hom.map_mul, coe_fn_coe_base]
-- works, `⇑f (a * b) ==> ⇑↑f (a * b) ==> ⇑↑f a * ⇑↑f b <== ⇑f a * ⇑f b`
example [monoid M] [monoid N]
  (f : M →* N) : f 1 = 1 := by simp only [one_hom.map_one, coe_fn_coe_base]
-- fails, `⇑↑f 1 = 1`

Eric Wieser (Mar 05 2021 at 12:47):

In your branch with both has_coe_to_fun on all but mul_hom / one_hom and coe_fn_coe_base removed, I assume?

Eric Wieser (Mar 05 2021 at 12:48):

In which case, it seems like coe_fn_coe_base is sort of a sanity check that users remembered to define the has_coe_to_fun to avoid that issue, even if it rarely comes up in actual use?

Anne Baanen (Mar 05 2021 at 12:49):

Shouldn't we leave these checks up to the linter, instead of having to diagnose it via simp timeouts? :P

Eric Wieser (Mar 05 2021 at 12:50):

Doesn't the presence of coe_fn_coe_base cause the simp linter to complain about downstream lemmas about types missing has_coe_to_fun?

Eric Wieser (Mar 05 2021 at 12:51):

Or does it cause the linter itself to timeout?

Anne Baanen (Mar 05 2021 at 12:55):

It causes simp calls to timeout if there are simp lemmas like docs#monoid_hom.to_mul_hom_coe that turn ⇑↑f back into ⇑f (in this case via ⇑(f.to_mul_hom).

Anne Baanen (Mar 05 2021 at 12:56):

I pushed 2 commits to GitHub if you want to check it out: https://github.com/leanprover-community/mathlib/commit/36ce6ed48233102677000c9ec42f6a31fc427892 and https://github.com/leanprover-community/mathlib/commit/38163b184a7e43dcc826a9d5cabfc672c7167a14

Anne Baanen (Mar 05 2021 at 14:56):

I looked into the build error if you remove the has_coe_to_fn instance, looks like simps doesn't realize that there still is a coercion to a function and starts generating lemmas of the form f.to_fun _ = _ instead of ⇑f _ = _.

Anne Baanen (Mar 05 2021 at 15:02):

Seems to be fixed when manually adding the projection, but I'm wondering why it's not found.

@[to_additive]
def monoid_hom.simps.to_fun {M N : Type*} [monoid M] [monoid N] : (M →* N)  M  N := coe_fn

Last updated: Dec 20 2023 at 11:08 UTC