coq / stdlib2

GNU Lesser General Public License v2.1
39 stars 9 forks source link

Naming conventions #2

Open maximedenes opened 6 years ago

maximedenes commented 6 years ago

@CohenCyril and I will extend https://github.com/math-comp/math-comp/blob/master/CONTRIBUTING.md#statements-of-lemmas-theorems-and-definitions

These conventions will be used in stdlib2.

Their description should be moved to the stdlib2 repository, and missing/unclear conventions can be discussed here.

andres-erbsen commented 6 years ago

"push" lemmas have often been named like mul_cos : cos(a)*cos(b) = (cos(a+b) + cos(a-b))/2, add_0_r, not_not_decidable. I find that naming scheme very intuitive and helpful, often allowing to skip a Search command. This scheme is also used quite widely in the current Coq library. I can see that the math-comp style is much more concise, but I am worried that this brevity might become an obstacle to new users. What are the other reasons for switching to that style?

maximedenes commented 6 years ago

Sorry, what do you mean by "push"?

A few remarks:

herbelin commented 6 years ago

I'd be interested in seeing a synthesis of all naming conventions we have around.

There is one here (to be eventually turned into a PDF).

If I remember correctly, @silene also had a proposal which he mentioned when we talked about naming conventions at a WG last year.

maximedenes commented 6 years ago

By the way, I don't think that there is a unique satisfactory naming convention. So, some arbitrary choice will be made at some point. We have so many technical points to solve that I don't expect to spend much time on naming conventions, although I will read all these documents to stay informed.

herbelin commented 6 years ago

I don't think that there is a unique satisfactory naming convention

I agree, and my conclusion about that was that the (challenging and interesting) way to address this heterogeneity of style is to turn naming conventions into languages of naming. But let's address things in order and start indeed by collecting the technical points to solve.

andres-erbsen commented 6 years ago

By "push" lemmas I mean the rules for commuting one operation from outside of other operations to inputs of these operations. Here are some push lemmas for Z.pow from the current standard library, named mostly as I described with the addition of _l and _r to distinguish two conceptually different inputs of Z.pow:

Z.pow_1_r: forall a : Z, (a ^ 1)%Z = a
Z.pow_0_l: forall a : Z, (0 < a)%Z -> (0 ^ a)%Z = 0%Z
Z.pow_0_r: forall n : Z, (n ^ 0)%Z = 1%Z
Z.pow_0_l': forall a : Z, a <> 0%Z -> (0 ^ a)%Z = 0%Z
Z.pow_opp_even: forall a b : Z, Z.Even b -> ((- a) ^ b)%Z = (a ^ b)%Z
Z.pow_2_r: forall a : Z, (a ^ 2)%Z = (a * a)%Z
Z.pow_opp_odd: forall a b : Z, Z.Odd b -> ((- a) ^ b)%Z = (- a ^ b)%Z
Z.pow_mul_l: forall a b c : Z, ((a * b) ^ c)%Z = (a ^ c * b ^ c)%Z
Z.pow_1_l: forall a : Z, (0 <= a)%Z -> (1 ^ a)%Z = 1%Z
Z.pow_succ_r: forall n m : Z, (0 <= m)%Z -> (n ^ Z.succ m)%Z = (n * n ^ m)%Z
Z.pow_twice_r: forall a b : Z, (a ^ (2 * b))%Z = (a ^ b * a ^ b)%Z
Z.pow_mul_r: forall a b c : Z, (0 <= b)%Z -> (0 <= c)%Z -> (a ^ (b * c))%Z = ((a ^ b) ^ c)%Z
Z.pow_add_r: forall a b c : Z, (0 <= b)%Z -> (0 <= c)%Z -> (a ^ (b + c))%Z = (a ^ b * a ^ c)%Z
Z.pow_sub_r: forall a b c : Z, a <> 0%Z -> (0 <= c <= b)%Z -> (a ^ (b - c))%Z = (a ^ b / a ^ c)%Z
Z.pow_div_l:
  forall a b c : Z, b <> 0%Z -> (0 <= c)%Z -> (a mod b)%Z = 0%Z -> ((a / b) ^ c)%Z = (a ^ c / b ^ c)%Z

I can't think of other naming conventions that I have clear opinions about, but this one has been very helpful in cases it is applicable. I don't particularly care that it is the only naming convention allowed for these lemmas, but I would find it unfortunate if names/aliases of this style were barred from inclusion in stdlib2. For more examples of this pattern, please see https://github.com/mit-plv/fiat-crypto/blob/4ecdd6ca43af688e5cd36ec9ab2496c4e192477d/src/Util/ZUtil/Hints/PullPush.v and https://github.com/mit-plv/fiat-crypto/search?q=push&unscoped_q=push

silene commented 6 years ago

@silene also had a proposal which he mentioned when we talked about naming conventions at a WG last year.

It was not so much a proposal than a description of the naming convention that we used in Coquelicot and in Flocq 3 and how it fared. The driving motivation was that the name of a theorem you need to apply to the current goal should be inferrable from the statement of the goal. I recall here the main principles for those who did not follow that WG:

  1. If the conclusion of a theorem statement starts with a named predicate, its name also starts with it.
  2. If the theorem is specific to some applied term, then the function name follows (unless it is not discriminating).
  3. Similarly, if the term is a universally quantified variable but it has to satisfy a given predicate, then the name of this predicate follows (unless it is not discriminating).
  4. If the predicate is an infix operator (e.g. order relation), rule 1 above is ignored and the operator is put into infix position.
  5. If, due to an infix operator, the first part of a name would be an integer, the converse infix operator is used, just for naming.
  6. Redundancies are avoided, unless discriminating.
  7. If the theorem is a rewriting rule, equality is not mentioned in the name. So the name mostly reflects the left-hand side.
  8. Right-hand sides of rewriting rules are either simpler than left-hand sides or more expanded. So the name mostly reflects the complicated term.
  9. If the name is still not unique at this point, discriminating parts of the hypotheses are used.

Here are a few examples (premises are skipped unless relevant):

That said, our approach is obviously flawed, since some theorems do not follow the above naming conventions.

To conclude, here are the main differences with MathComp's naming convention (and why we did not want to follow it):

herbelin commented 6 years ago

A concrete case about naming is given in coq/coq#8815, which, among others, adds the following NArith lemma:

Lemma succ_double_le n m : n <= m -> succ_double n <= succ_double m.

Coquelicot/Flocq would say succ_double_le.

If I understood correctly, MathComp rules would say le_succ_double (this policy is effectively used, e.g. for binary functions, in ssrnat.ltn_mul and ssrnum.ler_add, though there are exceptions, e.g. binomial.fact_smonotone, or falgebra.expvS).

Stdlib1 has many variants for such a lemma, such as BinNat.succ_double_lt, Ndec.Nleb_double_plus_one_mono, or, for other unary operators, e.g. for succ: BinPos.succ_lt_mono or Zorder.Zsucc_lt_compat; for Zpower: Zpow_facts.Zpower_le_monotone (a Frenchism); for pow: Rfunctions.Power_monotonic. The naming proposal in dev/doc/naming-conventions.tex would be quite verbose and say succ_double_lt_compat or double_plus_one_lt_compat.

So, maybe succ_double_le is indeed a good compromise (relatively short, as well as non-ambiguous, once we have learned the convention which distinguishes succ_double_le : n <= m -> succ_double n <= succ_double m and le_succ_double : succ_double n <= succ_double m -> n <= m)?

Any opinion in the context of stdlib2?

vbgl commented 6 years ago

About this particular example, I doubt that these lemmas should be in the standard library. Instead, a unique and more general (n.×2+1 ≤ m.×2+1) = (m ≤ n) seems also more convenient to use.

herbelin commented 6 years ago

About this particular example, I doubt that these lemmas should be in the standard library. Instead, a unique and more general (n.×2+1 ≤ m.×2+1) = (m ≤ n) seems also more convenient to use.

Good questions! Can you detail your vision? What should be the extent of a "standard library"? What should be the place of decidable relations? Can you give examples?