Zulip Chat Archive

Stream: general

Topic: Bad instance name lint


Mario Carneiro (Aug 12 2020 at 18:15):

This has caught me out many times, so I'm wondering if we can get a lint to watch for it (or maybe even fix lean so it doesn't happen).

inductive widget : Type
inductive doodad : Type

instance : has_to_string widget := ⟨λ x, by cases x -- widget.has_to_string
instance : has_to_string doodad := ⟨λ x, by cases x -- doodad.has_to_string

namespace foo

inductive widget : Type
inductive doodad : Type

instance : has_to_string widget := ⟨λ x, by cases x -- foo.has_to_string
instance : has_to_string doodad := ⟨λ x, by cases x -- foo.has_to_string, error

end foo

Usually I won't even notice that foo.widget's instance has a name that doesn't mention widget until I write foo.doodad and hit the name conflict, so I'm quite sure that there are many uncaught instances of this problem in mathlib.

Kevin Buzzard (Aug 12 2020 at 18:23):

ha ha I'm not sure I want to know the number of instances with bad names in mathlib :-) Wouldn't it be more fun to change Lean so that it names the instances in a cleverer way and then see if mathlib still compiles? So what is the algorithm for naming instances? It's well-known that the current method is laughed at but is there a cleverer proposal which is less bad? In namespace foo I decide to prove foo A -> baz B -> bar (prod A B) and also bar A -> foo B -> baz (prod A B) (with foo, bar, baz all classes). Are these now both named something like foo.prod? What is your proposal for the example you post? What do you want has_to_string foo to be called in namespace foo? I think there was a discussion here a long time ago about naming lemmas and how it wasn't at all automatic because you have a tree of constants which you want to turn into a list of constants.

Mario Carneiro (Aug 12 2020 at 18:57):

I think that for the most part the instance names that are automatically generated are okay, not great but usable

Mario Carneiro (Aug 12 2020 at 18:58):

not to mention that they more or less by necessity have to define our style guide for naming instances

Mario Carneiro (Aug 12 2020 at 19:01):

I think that the right answer in this instance is that namespaces should always just append, never replace bits of the name. So this one should be called foo.widget.has_to_string

Mario Carneiro (Aug 12 2020 at 19:02):

Although, I think that this would name your instances foo.prod.bar and foo.prod.baz respectively which is a bit odd to say the least

Mario Carneiro (Aug 12 2020 at 19:05):

Another thing that would help is if the instance naming algorithm was more comfortable saying "I don't know what to call this, you do it"

Mario Carneiro (Aug 12 2020 at 19:07):

For example

instance : has_to_string (Sort -> Sort) := sorry

gives failed to synthesize instance name, name should be provided explicitly but

namespace foo
instance : has_to_string (Sort -> Sort) := sorry
end foo

gives foo.has_to_string like the others. I think that when this situation occurs (lean wants to call it foo.has_to_string) we are in the case that we should at least lint and maybe also give the failed to synthesize instance name error

Kevin Buzzard (Aug 12 2020 at 19:13):

I think I'd rather be asked for an instance name than accidentally name an instance badly

Mario Carneiro (Aug 12 2020 at 19:17):

Here is the relevant code:

        /* Try to synthesize name */
        expr it = type;
        while (is_pi(it)) it = binding_body(it);
        expr const & C = get_app_fn(it);
        name ns = get_namespace(p.env());
        if (is_constant(C) && !ns.is_anonymous()) {
            c_name = const_name(C);
            scope.set_name(c_name);
        } else if (is_constant(C) && is_app(it) && is_constant(get_app_fn(app_arg(it)))) {
            c_name = const_name(get_app_fn(app_arg(it))) + const_name(C);
            scope.set_name(c_name);
        } else {
            p.maybe_throw_error({"failed to synthesize instance name, name should be provided explicitly", c_pos});
            c_name = mk_unused_name(p.env(), "_inst");
        }

In english, it says to get the conclusion of the theorem, and call it it, with the head constant of it (the instance like has_to_string) being C. If we are not at the top level, then name it current_namespace.C (this is the bad case), else if the last argument of it is itself a constant D applied to things then name it current_namespace.D.C, else complain to the user

Mario Carneiro (Aug 12 2020 at 19:19):

We could just swap the two if statements, so that the present case gets the name current_namespace.D.C, and the scheme current_namespace.C is only used for things like the has_to_string (Sort -> Sort) example

Mario Carneiro (Aug 12 2020 at 19:19):

or we could just delete that case entirely so that this case gives the failed to synthesize instance name error

Reid Barton (Aug 29 2020 at 16:48):

I think the first case probably exists to handle situations like

structure foo : Type
namespace foo
instance : has_add foo := ⟨λ _ _, ⟨⟩⟩
end foo
#check foo.has_add   -- not `foo.foo.has_add`

because it's pretty common to create a structure and then add a lot of things in a namespace, and some of those things are likely to be instances for the structure you just made.

Reid Barton (Aug 29 2020 at 16:50):

Of course it can also choose the wrong name in other situations, for example if the namespace is something like category_theory

Reid Barton (Aug 29 2020 at 16:55):

How about this heuristic: drop the first case and, in the second case, don't include the D part of the name if it equals the last component of the current namespace.

Reid Barton (Aug 29 2020 at 17:56):

A better version of this is: drop the current namespace from either the C or the D component if it happens to be a prefix of them

Reid Barton (Aug 29 2020 at 20:50):

lean#458 is my proposal

Reid Barton (Aug 30 2020 at 00:39):

wait, no. I was supposed to say "My proposal is lean#458"


Last updated: Dec 20 2023 at 11:08 UTC