Zulip Chat Archive
Stream: lean4
Topic: rfc: theorem names
Daniel Selsam (May 04 2021 at 01:03):
There is a proposal for theorem names at https://github.com/leanprover/lean4/issues/402 The proposal is snake case but with camelCase for the components:
nat_add_zero
axiom_of_choice
eq_punit
runCatch_pure
seqLeft_eq_bind
Note that types (e.g. Nat
, Eq
) become lowercase. The known downside is that it cannot distinguish between e.g. Or
and or
, but this may acceptable in light of how bad the alternatives seem. If anybody sees any critical problems with this proposal, please share them in the GitHub issue.
Mario Carneiro (May 04 2021 at 01:15):
Why nat_add_zero
instead of Nat.add_zero
?
Mario Carneiro (May 04 2021 at 01:16):
specifically, why is it no longer in the Nat
namespace?
Mario Carneiro (May 04 2021 at 01:19):
When you say "types became lowercase", do you mean Nat
becomes nat
like in lean 3? Or is this only for theorem names, not the actual type names?
Daniel Selsam (May 04 2021 at 01:22):
Mario Carneiro said:
Why
nat_add_zero
instead ofNat.add_zero
?
I just translated what happened to be in Init
to the proposed style. Nat.add_zero
is consistent with the proposal.
Daniel Selsam (May 04 2021 at 01:22):
Whatever comes between the _
would be camelCase.
Mario Carneiro (May 04 2021 at 01:23):
So types, modules, and namespaces are all PascalCase?
Daniel Selsam (May 04 2021 at 01:23):
Yes, and file names.
Mario Carneiro (May 04 2021 at 01:26):
There will be a number of specific naming conflicts to resolve, no matter what the convention is. I guess the RFC isn't the appropriate place for this, although it might influence the choice
Mario Carneiro (May 04 2021 at 01:27):
I'm broadly supportive of the plan though
Daniel Selsam (May 04 2021 at 01:28):
It would be helpful to find other examples of the Or_True
/or_true
clash, besides just Prop
/Bool
.
Mario Carneiro (May 04 2021 at 01:28):
Are we using Title_Snake_Case
anywhere?
Daniel Selsam (May 04 2021 at 01:28):
i.e. names that appear both uppercase as types and lowercase as values/functions
Daniel Selsam (May 04 2021 at 01:29):
No, we are not using Title_Snake_Case
anywhere. That is option (2) in the GitHub issue. It has very little support AFAICT, in part because of the issue I mentioned: connectives (and, or) and verbs (eq) are awkward capitalized.
Mario Carneiro (May 04 2021 at 01:30):
when you say Or_True
then are you referring to the theorem p \/ True = True?
Daniel Selsam (May 04 2021 at 01:30):
Yes, and or_true
is p || true = true
.
Mario Carneiro (May 04 2021 at 01:31):
That looks like Bool.or_true
Mario Carneiro (May 04 2021 at 01:32):
and I think something similar to this can avoid most of the name clashes about Prop/Bool
Mario Carneiro (May 04 2021 at 01:32):
we could also go back to tt
/ff
Daniel Selsam (May 04 2021 at 01:32):
I agree that Bool.or_true
is a fine solution for that particular clash.
Mario Carneiro (May 04 2021 at 01:33):
The fact that true
is blue and True
isn't seems like it implicitly prefers true
, which probably isn't the right default for mathematics
Mario Carneiro (May 04 2021 at 01:34):
Well, maybe we can get by if we just use ⊤
and ⊥
for true and false, although those symbols are currently in use for lattices
Daniel Selsam (May 04 2021 at 01:35):
Daniel Selsam said:
i.e. names that appear both uppercase as types and lowercase as values/functions
Here is one possible clash:
inductive EvalsTo (p : Program) (v : Value) : Prop
def evalsTo (p : Program) (n : Nat) : Option Value
theorem evalsTo_of_evalsTo
Mario Carneiro (May 04 2021 at 01:35):
I would just use EvalsTo
in the theorem name if there is a name clash like that
Mario Carneiro (May 04 2021 at 01:36):
I think it's fine to just use that as a backup plan
Mario Carneiro (May 04 2021 at 01:36):
there are quite a few backup plans in the mathlib naming convention
Mario Carneiro (May 04 2021 at 01:37):
What about lowercasing Prop
-valued types?
Daniel Selsam (May 04 2021 at 01:37):
Mario Carneiro said:
The fact that
true
is blue andTrue
isn't seems like it implicitly preferstrue
, which probably isn't the right default for mathematics
Neither are blue for me. Is that a VSCode thing? What does blue mean?
Mario Carneiro (May 04 2021 at 01:37):
keyword I guess
Mario Carneiro (May 04 2021 at 01:37):
even though it's not a keyword
Daniel Selsam (May 04 2021 at 01:38):
Could be easy to just make True
and true
blue then
Mario Carneiro (May 04 2021 at 01:39):
true
is apparently in the constant.language.lean4
textmate scope
Mario Carneiro (May 04 2021 at 01:39):
which is blue in my theme
Daniel Selsam (May 04 2021 at 01:39):
Mario Carneiro said:
What about lowercasing
Prop
-valued types?
You mean Prop
-valued inductive types?
Mario Carneiro (May 04 2021 at 01:40):
defs too, although I guess most of them are inductive
Mario Carneiro (May 04 2021 at 01:41):
someone pointed out earlier that propositions often feel more like functions than types
Mario Carneiro (May 04 2021 at 01:45):
Would it be alright to take a poll? I think this is the sort of thing that deserves a decent size poll
Daniel Selsam (May 04 2021 at 01:47):
Poll how?
Mario Carneiro (May 04 2021 at 01:47):
zulip has a poll system
Mario Carneiro (May 04 2021 at 01:47):
it might not get enough people though
Scott Morrison (May 04 2021 at 02:26):
(there may be more lurkers on this stream than you realise :-)
Mac (May 04 2021 at 02:29):
Here is a copy of my comment on the issue:
I personally prefer option 2, but option 3 is fine as well. However, since the major problem with 2 is ugly verbs, I would suggest mixing 2 and 3. That is, I would preserve the original casing for nouns and use lower camelCase for verbs (i.e.
Foo_and_Bar_iff_Rig
instead offoo_and_bar_iff_rig
andPUnit.eq_PUnit
instead ofPUnit.eq_punit
). I would then have the namespace a given theorem is in dictate the meaning of verbs. That is,Bool.or_true
means Boolor
and rootFoo_and_Bar_iff_Rig
means PropAnd
andIff
.
I agree with @Mario Carneiro that namespace can help resolve some of the clashes. I, however, would also like to see casing preserved at least for nouns.
Andrew Kent (May 04 2021 at 15:33):
FWIW, I am biased towards more predictable and case preserving of names/types/etc, so I guess (2)?. (3) is not bad though and perhaps with namespaces adding additional clarity at times it would be quite nice.
W.r.t. not liking (2), I don't think I fully understand the objections. So far I've seen
- it is not aesthetically pleasing (I guess it just doesn't bother me in what I've seen so far :shrug: ), and
- "Option 2 ... does not abide by the rule that only values in Sort should be capitalized" mentioned by @Sebastian Ullrich in the GH issue -- do these structures/classes in
Prop
also count as not abiding by this rule:
structure Iff (a b : Prop) : Prop where
intro :: (mp : a → b) (mpr : b → a)
class inductive Nonempty (α : Sort u) : Prop where
| intro (val : α) : Nonempty α
class Subsingleton (α : Sort u) : Prop where
intro :: allEq : (a b : α) → a = b
structure Equivalence {α : Sort u} (r : α → α → Prop) : Prop where
refl : ∀ x, r x x
symm : ∀ {x y}, r x y → r y x
trans : ∀ {x y z}, r x y → r y z → r x z
Or am I misunderstanding the naming rule w.r.t. values in Sort
and Prop
?
Sebastian Ullrich (May 04 2021 at 15:36):
What part do you mean, the class/structure names?
Sebastian Ullrich (May 04 2021 at 15:36):
Recall that Prop == Sort 0
:)
Andrew Kent (May 04 2021 at 15:41):
Yes, the structure/class names was what I was referring to. I suppose I don't have an intuition clicking in my head yet for when the aforementioned rule that (2) violates applies in practice as I'm skimming Lean files and how option 2 relates.
Sebastian Ullrich (May 04 2021 at 15:43):
Said otherwise, the rule is that declarations that are types (not just Type
s but also Prop
s) should be capitalized. Ne_Eq
is not a type.
Andrew Kent (May 04 2021 at 16:43):
I guess my vote is for (3) then :+1:
Daniel Selsam (May 24 2021 at 20:52):
There seems to be consensus for (3). Anyone have any lingering concerns/suggestions before we merge?
Mario Carneiro (May 25 2021 at 01:46):
I've mentioned this in private messages (and briefly above), but just to put it out there more formally and gauge interest: I'd like to propose a variant of (3) in which types that are propositions (i.e. of type Prop
or ... -> Prop
, but not necessarily types defeq to that like Set A
) are lowercase camel-cased, including in particular eq
, or
, and
, true
, false
.
- We can tell at a glance whether a type is a type or a prop, which is important information due to the effects of proof irrelevance. Currently mathlib indicates this inconsistently using the
is_
prefix. - It avoids the issue with
eq
in theorems being capitalized differently than the typeEq
. - It doesn't differ significantly from the current Haskell-ish convention for programming uses, where all the data types are actual types and so get the uppercase camel case style. In particular
Nat
,Int
and such are still capitalized. - This does lead to a clash with
Bool.true
. Personally I'd be fine with returning toBool.tt
but I don't think this is a particularly important issue and would hate for the whole naming convention to get blocked for a reason like this.
We can use this post as a :+1: :-1: mini-poll, where :-1: means option (3) as originally written.
Daniel Selsam (May 25 2021 at 01:59):
Based on other off-channel discussions, I think tt
/ff
is widely considered unacceptable for programming and a non-starter. At some point, you proposed weird names for Prop
true
/false
and always using notation for them.
Daniel Selsam (May 25 2021 at 02:03):
FWIW I don't think your first two points are that strong, since (a) you still don't know at a glance due to defeq, and (b) we will still have e.g. Nat
referred to as nat
in proof names. Also, this proposal would break the namespace/filename symmetry, since presumably filenames will still be capitalized but here there would be lowercase namespaces, e.g. prime
.
Mario Carneiro (May 25 2021 at 02:17):
If Bool.tt
isn't acceptable, using True
for the prop is close enough for me. We might be able to use top and bot symbols but I expect that these will be overloaded for lattices, so for core lean just True
and False
for the props should do.
(a) you still don't know at a glance due to defeq
I still don't really follow this point. Just because two types are defeq doesn't mean they act the same; in lean 3 it makes quite a difference and there are many redefinitions of quot.mk
for particular quotient types specifically so that they have a different syntactic type. The only thing that matters is the syntactic type, and for the edge cases we can leave it as a judgment call beyond the broad strokes of the naming convention.
Floris van Doorn (May 25 2021 at 02:17):
@Mario Carneiro: When using option (3) it doesn't matter that much whether Eq
is capitalized or not, right? We don't write Eq
, since we use the infix notation =
. And in all theorem names ..._eq...
will not be capitalized.
So the main differences I see will be namespace Eq
vs namespace eq
and if we have to refer to a full name for some reason (Eq.refl
vs eq.refl
).
If those are the main changes, I don't really care that much about this distinction. Maybe some full names are more common (Or.elim
?) though.
Mario Carneiro (May 25 2021 at 02:21):
(b) we will still have e.g. Nat referred to as nat in proof names.
It's pretty rare for Nat
to show up in the name of a theorem; it usually appears in the namespace of the theorem when the theorem is about a definition in the Nat
namespace. I consider the exceptions rare enough to be acceptable.
@Floris van Doorn Yes, the main place this shows up is in things like Or.inl
and Eq.symm
, although I think it will become more common as we start to get deeper into mathlib and encounter predicates and relations and prop-classes that have no notation
Daniel Selsam (May 25 2021 at 02:31):
Here is another argument in favor of @Mario Carneiro's proposal: many functions returning Prop
are decidable and used in programming in lieu of Bool
s. Currently we have an exception in the naming conventions for these, e.g. isValidChar
.
Daniel Selsam (May 25 2021 at 02:35):
FWIW I don't think eq.refl
vs Eq.refl
matters much though, and would probably rather Eq.refl
for uniformity (e.g. making all namespaces and filenames PascalCase)
Mario Carneiro (May 25 2021 at 02:38):
If the type is called eq
I think we should make the theorems eq.refl
as well, because otherwise we will end up with inconsistency when using dot notation (unless the mechanism for dot notation is changed so that it doesn't depend on naming in this way). Note that it is already not the case that all namespaces are uppercase - internal definitions will use the name of the main definition with an additional name segment like Namespace.mainDef.internalDef
Mario Carneiro (May 25 2021 at 02:41):
Regarding file names, I don't think we need to change anything - file/module names have never been related in any direct way to type names
Daniel Selsam (May 25 2021 at 02:42):
To clarify: I didn't mean eq
together with Eq.refl
, they should definitely agree.
Mario Carneiro (May 25 2021 at 02:44):
I'm not really sure whether project names (e.g. in the repo name and/or the name = ...
in the toml) need to be capitalized though. Personally I would like these to be lowercase and unrelated to lean module names, but I'm not sure if leanpkg wants some agreement here
Mario Carneiro (May 25 2021 at 02:50):
Note that it is already not the case that all namespaces are uppercase - internal definitions will use the name of the main definition with an additional name segment like
Namespace.mainDef.internalDef
While these are namespaces in the technical sense, I think we should distinguish between these uses of namespaces for grouping vs namespaces for indicating that one definition/theorem is about the one with the parent name. The former kind of namespace is somewhat rare in mathlib but there are some like category_theory
and these should be pascal case, and the latter kind of namespace should just follow the casing of the parent definition
Floris van Doorn (May 25 2021 at 02:53):
Daniel Selsam said:
Here is another argument in favor of Mario Carneiro's proposal: many functions returning
Prop
are decidable and used in programming in lieu ofBool
s. Currently we have an exception in the naming conventions for these, e.g.isValidChar
.
I think that is a pretty strong argument in favor of Mario's proposal. And this exception could be drawn much further (True
is definitely decidable, and And
preserves decidability). In that case it seems simplest to make all propositions camelCase
.
Mario Carneiro (May 25 2021 at 02:59):
Another solution to True
/true
that just occurred to me: we can have a notation true
with a type-dependent elaborator that elaborates to True
when the target type is Prop
and Bool.true
or its coercion otherwise. Lean 3 already makes it fairly transparent to use true
for tt
or vice versa, but the coercion that sneaks in bothers me and so I try to always use the right one. A type dependent elaborator would let me use true
always with a good conscience
Daniel Selsam (May 25 2021 at 03:22):
Floris van Doorn said:
Daniel Selsam said:
Here is another argument in favor of Mario Carneiro's proposal: many functions returning
Prop
are decidable and used in programming in lieu ofBool
s. Currently we have an exception in the naming conventions for these, e.g.isValidChar
.I think that is a pretty strong argument in favor of Mario's proposal. And this exception could be drawn much further (
True
is definitely decidable, andAnd
preserves decidability). In that case it seems simplest to make all propositionscamelCase
.
Do you also agree with Mario's proposal that this should only extend to identifiers that syntactically return Prop
and not to ones that return e.g. Set
s? So it would be "bad style" to declare isValidChar : Set Nat
and then use it as a boolean?
Mario Carneiro (May 25 2021 at 03:35):
It's already bad style in mathlib to write isValidChar x
if isValidChar : Set Nat
. You would instead have validChars : Set Nat
and x \in validChars
to express that. (EDIT: validChars
should be lowercase because it's not a type)
Mario Carneiro (May 25 2021 at 03:36):
I don't know if we have a linter for that but we should
Daniel Selsam (May 25 2021 at 03:39):
Huh? I thought you were specifically proposing ValidChars : Set Nat
, i.e. you only camelCase things that syntactically return Prop
.
Mario Carneiro (May 25 2021 at 03:40):
:thinking: That last example has me wondering whether we need another exception to the rule to capitalize things of type Set A
since they are "like" types even though they aren't, technically.
Mario Carneiro (May 25 2021 at 03:42):
@Daniel Selsam The rule is to capitalize things of type Sort u
or Type u
but not Prop
or OtherType
. Absent another exception that means that sets should be lowercase
Daniel Selsam (May 25 2021 at 03:42):
Wasn't that exception in your proposal?
I'd like to propose a variant of (3) in which types that are propositions (i.e. of type Prop or ... -> Prop, but not necessarily types defeq to that like Set A) are lowercase camel-cased
Mario Carneiro (May 25 2021 at 03:45):
I think using Set A
in that example was not a good choice, because if we "see though" the abbreviation then it has type A -> Prop
so it should be lowercase, and if we don't then it has type Set A
which is not a universe so it should also be lowercase
Mario Carneiro (May 25 2021 at 03:45):
what I meant to say by that example is that the naming convention does not "see through" abbreviations
Daniel Selsam (May 25 2021 at 03:47):
Ah, thanks for clarifying. I understand now.
Scott Morrison (May 25 2021 at 03:48):
Slippery slope: if we were to capitalize things of type Set A
since they are like types, wouldn't we also want to capitalize things of type Submodule R M
, etc, since they are just as much like types?
Daniel Selsam (May 25 2021 at 03:48):
What about capitalizing things that syntactically return something capitalized besides Prop
?
Mario Carneiro (May 25 2021 at 03:49):
that would include Nat.add
Mario Carneiro (May 25 2021 at 03:49):
Re: slippery slope, we could extend the rule (whatever it is) to things with a coe to sort
Daniel Selsam (May 25 2021 at 03:52):
Mario Carneiro said:
Daniel Selsam The rule is to capitalize things of type
Sort u
orType u
but notProp
orOtherType
. Absent another exception that means that sets should be lowercase
I don't yet see a major problem with this. We can simplify the wording as only capitalize things that syntactically return Sort u
or Type u
.
Mario Carneiro (May 25 2021 at 03:52):
I don't have a well formed opinion on sets, submodules etc. Maybe lowercase is fine
Daniel Selsam (May 25 2021 at 03:53):
I think it would be a priori reasonable to use decidable Set
s in programming, i.e. n \in validChars
.
Mario Carneiro (May 25 2021 at 03:54):
I don't think that resolves the question though. Both capital and lowercase seem reasonable in that position
Daniel Selsam (May 25 2021 at 03:55):
It would be behaving like a datastructure though (which would normally be camelCase), similar to how isValidChar
would be behaving like a Bool
-valued function.
Mario Carneiro (May 25 2021 at 03:56):
One argument for the slippery slope version: if there is a coe to sort, then you can write n: validChars
and with the lowercasing rule for props this makes n
look like a proof of a proposition
Mario Carneiro (May 25 2021 at 03:58):
So to more formally state the slippery slope version: Only capitalize things that either syntactically return Sort u
or Type u
, or types that coerce to Sort u
or Type u
.
Mac (May 25 2021 at 06:03):
Mario Carneiro said:
Another solution to
True
/true
that just occurred to me: we can have a notationtrue
with a type-dependent elaborator that elaborates toTrue
when the target type isProp
andBool.true
or its coercion otherwise. Lean 3 already makes it fairly transparent to usetrue
fortt
or vice versa, but the coercion that sneaks in bothers me and so I try to always use the right one. A type dependent elaborator would let me usetrue
always with a good conscience
What is wrong with following?
universe u
class True (A : Type u) := true : A
class False (A : Type u) := false : A
export True (true)
export False (false)
instance : True Bool := ⟨Bool.true⟩
instance : False Bool := ⟨Bool.false⟩
instance : True Prop := ⟨Prop.true⟩
instance : False Prop := ⟨Prop.false⟩
(I am not sure whether Prop's should be a default instance, but the idea in general seems sound).
Mario Carneiro (May 25 2021 at 06:06):
The issue is that we want the result to be syntactically either True
or Bool.true
depending on the context. Since there are coercions in both directions you can still use either one in the wrong context and get something equivalent to the right result, but it is syntactically incorrect (if you use true : Prop
you get true = true
and if you use True : Bool
you get decide True
)
Mac (May 25 2021 at 06:08):
How does that relate to my class example?
Mario Carneiro (May 25 2021 at 06:08):
With an instance you would end up with the wrong answer in both cases, namely @True.true Prop instTrueProp
and @True.true Bool instTrueBool
instead of True
and Bool.true
respectively
Mac (May 25 2021 at 06:10):
Is that a problem?
Mac (May 25 2021 at 06:10):
They can be proved equivalent, right?
Mac (May 25 2021 at 06:11):
ex. example : true = Prop.true := rfl
or example : true = Bool.true := rfl
Mac (May 25 2021 at 06:22):
Furthemore, this seems to fit with the way Lean generally handles shared syntax (ex. the type classes for Add
, Mul
, etc.)
Mario Carneiro (May 25 2021 at 06:23):
Yes, it causes problems for simp, rw and other tactics that look at the syntax of a term in order to decide what to do
Mario Carneiro (May 25 2021 at 06:24):
I don't think the way to solve the issue of two ways to write true
is to make four ways to write true
Mac (May 25 2021 at 06:24):
Why isn't that also a sticking point for the math operators (and natural literals) then?
Mac (May 25 2021 at 06:25):
Sure you could make "true"/"false" custom syntax and do type-dependent elaboration as you said. But as Lean doesn't do that other syntax, but instead use type classes, it seems reasonable to use type classes here.
Mario Carneiro (May 25 2021 at 06:26):
This is getting off topic for this thread
Mac (May 25 2021 at 06:26):
Mario Carneiro said:
I don't think the way to solve the issue of two ways to write
true
is to make four ways to writetrue
No, now there is really one way -- everything would just use the true
from the type class. Just like everything uses the hAdd
from HAdd
when doing addition.
Mac (May 25 2021 at 06:27):
However, I am not denying there may be issues with that, it just seems reasonable to remain consistent with how this kind of polymorphism is done elsewhere.
Mario Carneiro (May 25 2021 at 06:28):
Any solution to this issue will be in mathlib anyway, lean core doesn't have to care
Mario Carneiro (May 25 2021 at 06:28):
lean can just define True
and Bool.true
Mario Carneiro (May 25 2021 at 06:29):
true
isn't notation (in lean core) so it doesn't make sense to have a notation typeclass for it (in lean core)
Mac (May 25 2021 at 06:29):
isn't the theorem names rfc about Lean core, though? so this will effect core.
Mario Carneiro (May 25 2021 at 06:29):
This is not an rfc about typeclass true
Mac (May 25 2021 at 06:30):
I am simply suggesting that this would be one way to resolve the true
/True
and false
/False
problem that came up as a sticking point for option (3). This would unify them into a single true
/false
thus removing the clash.
Mario Carneiro (May 25 2021 at 06:31):
your proposal is a lot more than a name change though, it needs separate discussion and this isn't the place for it
Mac (May 25 2021 at 06:33):
k, if you say so
Mario Carneiro (May 25 2021 at 06:33):
regardless of the existence of alternate typeclasses, you would still need a name for Prop.true
and Bool.true
. We don't use Prop
as a namespace elsewhere
Mario Carneiro (May 25 2021 at 06:33):
current frontrunner seems to be True
and Bool.true
Mac (May 25 2021 at 06:34):
Personally, I do think that using Prop
as a namespace would be a good idea and help mitigate a lot of these naming clashes.
Mac (May 25 2021 at 06:35):
As theorems that clash between Bool
and Prop
could go into their separate namespaces.
Mac (May 25 2021 at 06:36):
Mario Carneiro said:
current frontrunner seems to be
True
andBool.true
Well, yeah, because that is currently how it is set up and thus it is easiest to keep it that way.
Mario Carneiro (May 25 2021 at 06:38):
Note that true
and Bool.true
aren't an option not because they clash directly, but because they clash when Bool.true
is exported. That's why we're considering separate names like true/tt
or True/true
. Prop.true
/Bool.true
would similarly cause clashing exported names
Mac (May 25 2021 at 06:38):
I concur largely with what you said earlier, I think that since Prop
"types" follow proof irrelevance it makes sense for them to be lower camelCase like objects instead of upper camelCase like types (and this is already some followed with things like isValidChar
).
Mac (May 25 2021 at 06:39):
Mario Carneiro said:
Note that
true
andBool.true
aren't an option not because they clash directly, but because they clash whenBool.true
is exported. That's why we're considering separate names liketrue/tt
orTrue/true
.Prop.true
/Bool.true
would similarly cause clashing exported names
Yes but if you had something like a type class to unify them you wouldn't need to export them.
Mac (May 25 2021 at 06:39):
Just like you don't need to export Nat.add
or Int.add
because Add
unifies them.
Mac (May 25 2021 at 06:43):
In fact, if they used namespace, you could potentially solve the syntactic problem by having true : A
and false : A
be elaborated to A.true
/ A.false
(ex. Prop.true
/Bool.true
).
Sebastian Ullrich (May 25 2021 at 08:50):
I have to say I like the current naming convention with capitalizing propositions for teaching. Using the same naming convention really drives home the whole "propositions are just types", which, yes, students are quick to forget about again even if it's the very first and central thing you tell them. I'd like to hear whether @Jakob von Raumer and @Marc Huisinga agree.
Sebastian Ullrich (May 25 2021 at 08:52):
I agree that using IsValidChar
in a conditional would probably look weird. On the other hand,
structure Char where
val : UInt32
valid : val.isValidChar
also looks quite weird to me, with a camelcase ident right of :
! I haven't really made up my mind about this yet.
Gabriel Ebner (May 25 2021 at 08:55):
Sebastian Ullrich said:
also looks quite weird to me, with a camelcase ident right of
:
!
It's also going to be lowercase if Char.isValidChar : Char → Bool
is a Boolean. This is only consistent.
Marc Huisinga (May 25 2021 at 09:04):
Sebastian Ullrich said:
Using the same naming convention really drives home the whole "propositions are just types", which, yes, students are quick to forget about again even if it's the very first and central thing you tell them. I'd like to hear whether Jakob von Raumer and Marc Huisinga agree.
I'm not sure if students will really remember this any better if the naming convention mirrors it :sweat_smile:
One thing I've also noticed during the lab is that students sometimes confuse False
with false
and True
with true
. Not necessarily because they missunderstand the difference between Bool
and Prop
, but because they forget the exact name and write down the first thing that comes to their mind.
Sebastian Ullrich (May 25 2021 at 09:25):
Almost makes you wonder if we do need both true
and True
to begin with. We could have
def true.intro : true := rfl
def false.elim : false → α := fun h => nomatch h
and together with pretty-printing away = true
usage would basically stay the same. For efficiency and indexing reasons we might want to instead use
inductive Bool.asProp : Bool → Prop where
| true : asProp true
but that would really be an implementation detail.
Kevin Buzzard (May 25 2021 at 09:32):
Sebastian Ullrich said:
Almost makes you wonder if we do need both
true
andTrue
to begin with.
Yeah I'd be behind that proposal. My students never use Bool
, we could just scrap it completely.
François G. Dorais (May 25 2021 at 10:54):
@Sebastian Ullrich Or perhaps Bool.AsProp
...
Gabriel Ebner (May 25 2021 at 11:03):
Sebastian Ullrich said:
Almost makes you wonder if we do need both [
false
andFalse
] to begin with.
For False
, it is convenient to have dot-notation, i.e. (not_lt_of_le h1 h2).elim
which wouldn't work out-of-the-box with a coercion from Bool. But maybe there's a way to make dot-notation see through Bool.asProp
.
Daniel Fabian (May 25 2021 at 11:06):
@Gabriel Ebner wouldn't (not_lt_of_le h1 h2).asProp.elim
work?
Gabriel Ebner (May 25 2021 at 11:09):
No? With the proposal, not_lt_of_le h1 h2
would have the type Bool.asProp false
(pretty-printed as false
). Dot-notation would then look for a declaration called Bool.asProp.elim
.
Daniel Fabian (May 25 2021 at 11:09):
ah, gotcha
Daniel Fabian (May 25 2021 at 11:10):
elim
could potentially be a type class, but I guess that's completely besides the point.
François G. Dorais (May 25 2021 at 11:18):
Why would it look for Bool.elim
? This currently works:
inductive Bool.asProp : Bool → Prop where
| true : asProp true
theorem T : 0 = 1 → Bool.asProp false := λ h => Nat.noConfusion h
#reduce (h : 0 = 1) → (T h).recOn (motive:=λ _ _ => _) Empty
-- 0 = 1 → Bool.asProp.rec Empty (_ : Nat.noConfusionType (Bool.asProp false) 0 1)
Gabriel Ebner (May 25 2021 at 11:24):
Ah sorry, it would look for Bool.asProp.elim
of course. But that's still not helpful for false.elim (even if you manage to reuse the recursor) , since you need to provide lots of extra arguments to the recursor.
François G. Dorais (May 25 2021 at 11:36):
This works:
inductive Bool.asProp : Bool → Prop where
| true : asProp true
theorem T : 0 = 1 → Bool.asProp false := λ h => Nat.noConfusion h
def Bool.asProp.elim {C : Sort _} : false.asProp → C := λ h => nomatch h
example (h : 0 = 1) : true = false := (T h).elim
Sebastian Ullrich (May 25 2021 at 11:42):
Calling it elim
is a bit weird when it's not applicable to all asProp
s. How about exfalso
? Doesn't that make the proof script even more readable than before?
Sebastian Ullrich (May 25 2021 at 11:43):
Or elimFalse
or...
Gabriel Ebner (May 25 2021 at 11:43):
exfalso
's actually a good name. Although I'd still prefer dot-notation to see through the asProp.
Sebastian Ullrich (May 25 2021 at 11:47):
There was some discussion about a [parent]
attribute that could be applied to structure fields for this, but it would need to be even more general in this case...
François G. Dorais (May 25 2021 at 12:00):
Speaking of names: the constructor Bool.asProp.true
should be protected or perhaps renamed Bool.asProp.trivial
.
Patrick Massot (May 25 2021 at 15:42):
Sebastian Ullrich said:
I have to say I like the current naming convention with capitalizing propositions for teaching. Using the same naming convention really drives home the whole "propositions are just types", which, yes, students are quick to forget about again even if it's the very first and central thing you tell them.
For me this is a really strong argument against this naming convention. I try very hard to completely hide the whole "propositions are just types" crazyness to my students.
Marc Huisinga (May 25 2021 at 16:01):
Patrick Massot said:
Sebastian Ullrich said:
I have to say I like the current naming convention with capitalizing propositions for teaching. Using the same naming convention really drives home the whole "propositions are just types", which, yes, students are quick to forget about again even if it's the very first and central thing you tell them.
For me this is a really strong argument against this naming convention. I try very hard to completely hide the whole "propositions are just types" crazyness to my students.
One example that came up in the course was that students proved properties about the natural numbers and then had to define monoids and instantiate Nat as a monoid.
We explained that propositions are types (in the sense that we can use the same language to work with them) and showed examples of propositions and regular structures which only contain data.
Given that information, we expected them to figure out that you can declare the monoid rules as fields of the structure as part of the exercise.
Is that crazy, or would you instead not mention "propositions as types" at all and always introduce all Lean features separately, once for propositions and once for types, even if they are used in very similar ways?
EDIT: I realize this is a bit off-topic :sweat_smile:
Jeremy Avigad (May 25 2021 at 16:02):
I'd like to cast a vote in favor of (at least trying out) the current proposal to reserve upper case for datatypes (and types like Set
that are thought of as mathematical data). Teaching and constructive ideology aside, propositions aren't datatypes. Conflating them is confusing, and in practice it is often useful to be able to distinguish them easily.
As Patrick's remark indicates, mathematicians feel strongly about this. But it is also useful for computer scientists to distinguish between programming and verification, and to be able to tell at a glance whether they are dealing with a piece of code or a specification.
I don't know the best way to mediate between Prop and Bool. Sometimes x < y
is a branching condition and sometimes it is used as a specification. But whatever we do to finesse that, I think it is helpful to be mindful of the distinction.
Jeremy Avigad (May 25 2021 at 16:06):
P.S. I have no problem with propositions as types -- it is useful as an analogy, and explaining commonalities in Lean syntax when writing code and proving theorems. All I am saying is that it is often helpful to distinguish them as well.
Kevin Buzzard (May 25 2021 at 16:33):
Am I right in thinking that propositions _aren't_ types in Isabelle/HOL?
"define monoids" would be something that people in my course would find easy, because I define lots of structures very early on -- mathematics is full of structures. My students have probably never seen the inductive
keyword though.
Mario Carneiro (May 25 2021 at 16:48):
Am I right in thinking that propositions _aren't_ types in Isabelle/HOL?
That's correct. In ZFC or HOL based systems (Isabelle/HOL, HOL Light, Mizar, Metamath) propositions are in a separate class from data / objects in the domain of discourse and you can't conflate them. Only DTT based systems (Coq, Agda, Lean) have propositions as types.
Mario Carneiro (May 25 2021 at 16:55):
Marc Huisinga said:
One thing I've also noticed during the lab is that students sometimes confuse
False
withfalse
andTrue
withtrue
. Not necessarily because they missunderstand the difference betweenBool
andProp
, but because they forget the exact name and write down the first thing that comes to their mind.
This is the audience I'm trying to address with the proposal for a type dependent elaborator for the notation true
. It doesn't come with an unexpander so it will still be printed as True
(maybe this is debatable), but it's easier to do the right thing when you can just always write true
and lean just inserts the right one.
Gabriel Ebner (May 25 2021 at 16:57):
Mario Carneiro said:
In ZFC or HOL based systems (Isabelle/HOL, HOL Light, Mizar, Metamath) propositions are in a separate class from data / objects in the domain of discourse and you can't conflate them.
Which is not entirely true. In the HOL systems, there is only a distinction between terms and types. The Booleans are a type, and e.g. true has the type Boolean. Just like the natural numbers are a type, and 0 has the type natural number. And propositions are just terms of type Boolean. (In metamath these are indeed different categories though.)
Gabriel Ebner (May 25 2021 at 16:58):
Another category is proofs: these are completely separate in all non-DTT systems that I know of.
Mario Carneiro (May 25 2021 at 16:58):
ah, you are right. In HOL propositions are objects in the domain of discourse, but proofs are not
Mario Carneiro (May 25 2021 at 17:02):
Actually maybe I should say that propositions are like data but they have a different type (namely Boolean
in HOL and wff
in metamath). This is more surprising in metamath since there are only a handful of types, but it's approximately the same distinction
Patrick Massot (May 25 2021 at 17:06):
In my teaching using Lean students don't define define anything. They don't even state lemmas. It's all about proofs (what else?).
Marc Huisinga (May 25 2021 at 17:07):
Patrick Massot said:
In my teaching using Lean students don't define define anything. They don't even state lemmas. It's all about proofs (what else?).
But that's only half the fun! :grinning_face_with_smiling_eyes:
Patrick Massot (May 25 2021 at 17:08):
My course is really using Lean as a tool towards learning how to write correct proofs on paper.
Sebastian Ullrich (May 25 2021 at 17:11):
Jeremy Avigad said:
But it is also useful for computer scientists to distinguish between programming and verification, and to be able to tell at a glance whether they are dealing with a piece of code or a specification.
This is in fact nicely demonstrated at Char
, where one really wants the information that the second field is erased and thus the entire structure is unboxed. So I suppose I just have to get used to it.
Daniel Selsam (May 26 2021 at 15:40):
Summary so far:
- Their seems to be consensus for the original (3), which is independent of the capitalization scheme for types/props.
- There seems to be consensus to (a) capitalize definitions that syntactically return
Sort u
orType u
and (b) not to capitalize definitions that syntactically returnProp
. One of the main benefits of the latter is that we are already lower-casing many of these anyway as special-cases, since decidableProp
s are frequently used likeBool
s. - There does not seem to be consensus yet whether to capitalize definitions that return other types, which includes both defs that are def-eq to something returning
Prop
(e.g.def mySet : Set Nat
) and ones that are not (e.g.def MapToType α := α → Type; def foo : MapToType Nat
). Mario proposed the "slippery slope" addendum: also capitalize defs that return types that coerce toSort u
orType u
. Another obvious candidate is the "semantic" approach of removing the "syntactic" qualifier from the original proposal. Note that these two proposal have the opposite behavior on the two examples above. - There are a few ways to deal with
true
, the simplest being to just capitalize theProp
versions. I think other variants could probably be considered separately from this issue.
Daniel Selsam (May 26 2021 at 15:53):
Downside to slippery slope proposal: the presence of a coercion isn't as binary/definitive as either the syntactic or semantic checks. Somebody could add a CoeSort
in some other file much later, or as a local instance, etc.
Mario Carneiro (May 26 2021 at 19:55):
Somebody could add a CoeSort in some other file much later, or as a local instance, etc.
I don't think these are very important considerations. The vast majority of the time whether something has a coe to sort or not is relatively integral to the type's expression and use. If CoeSort
is added in a file much later, that's bad style, unless for whatever reason it is very difficult to set up the coercion in which case the whole file should be written with that in mind. If it's a local instance, then it doesn't count.
Mario Carneiro (May 26 2021 at 19:55):
Since we're talking about a naming convention here it's okay to have a solution for 90% of cases and leave the rest as judgment calls
Daniel Selsam (May 26 2021 at 20:15):
I agree this is unlikely to be an issue. I think I would still rather foo : MapToType Nat
to be Foo
though. How about slippery-slope + semantic, since they seem to be compatible. The rule would be caps if it semantically returns Sort u
or Type u
, or if it coe-to-sorts to something that does.
Mario Carneiro (May 26 2021 at 20:27):
The main issues I see with the semantic approach:
- Definitions can be arbitrarily stacked, so it can be unobvious and sometimes even surprising to discover that something, ultimately, boils down to a function to Type/Prop. This is well known as the "apply bug" in lean 3, where the
apply
tactic uses the number of pi types in the fully unfolded type of the applied lemma even though most of them are hidden to the user (and in some cases even explicitly marked as opaque using@[irreducible]
). The purpose of definitions is to encapsulate complexity and we shouldn't be peeking behind the curtain. - Besides this, it can actually just be difficult to determine "at a glance" whether a type is such a definition, which means that people will be tempted to use a lower cost approximation to the naming convention, leading to inconsistency.
- For abbreviations, there is a stronger argument for unfolding since the user is explicitly indicating that it should be unfolded. However that's a directive to lean, not to readers, and I would still be inclined to default to not unfolding in this case with exceptions added on a case-by-case basis.
Daniel Selsam (May 27 2021 at 03:43):
Mario Carneiro said:
- Definitions can be arbitrarily stacked, so it can be unobvious and sometimes even surprising to discover that something, ultimately, boils down to a function to Type/Prop.
Can you please clarify why this wouldn't be compositional? Why couldn't you just look at the capitalization of the syntactic return type?
def Foo0 (α : Type) : Type 4 := α → Type 3
def Foo1 : Foo0 Nat := fun _ => Type 2
def Foo2 : Foo1 0 := Type 1
def Foo3 : Foo2 := Type
Mario Carneiro (May 27 2021 at 03:59):
Foo2
is capitalized the same as Nat
but def foo3 : Nat
and def Foo3 : Foo2
have different capitalization
Mario Carneiro (May 27 2021 at 04:01):
To determine how Foo3
needs to be capitalized you have to look at the definition (value) of Foo2
and unfold everything there (your example is just Type 1
so no additional unfolding is necessary)
Mario Carneiro (May 27 2021 at 04:04):
A bad case would be something like this:
def Foo0 (α : Type) : Type 1 := α → Type
def Foo1 : Type 1 := Foo0 Nat
def Foo2 : Type 2 := Foo1 -> Foo1
def Bar : Foo2 := id
to determine that Bar
should be capitalized you need to look at the definition of Foo0
, Foo1
and Foo2
, and none of the capitalization there helps
Daniel Selsam (May 27 2021 at 04:06):
Whoops, I confused myself. Thanks for clarifying. In that case I agree with the syntax + slippery-slope proposal. I will summarize it on the GitHub issue.
Mac (May 27 2021 at 10:34):
Question: If we are proposing to capitalize any identifier that is syntactically in Sort u
or Type u
, wouldn't it make sense for auto bound implicit arguments to auto bind single upper case letters as Sort u
/Type u
rather than single lower case letters (as having a lower case identifier syntactically in Sort u
/Type u
violates this style guide)?
Sebastian Ullrich (May 27 2021 at 10:39):
This RFC is about naming declarations. I haven't seen any proposals for changing our parameter naming scheme, which is to always use lowercase names, mostly Greek ones for type parameters.
Sebastien Gouezel (May 27 2021 at 10:43):
It's not exactly what we do in mathlib, where our style guide contains: "α, β, γ, ... for generic types. Types with a mathematical content are expressed with the usual mathematical notation, often with an upper case letter (G for a group, R for a ring, K or 𝕜 for a field, E for a vector space, ...)".
But it's arguably a mathlib thing, so none of the concern of core.
Mac (May 27 2021 at 10:44):
It just seems a little odd to me to auto bind lower case ASCII letters as Type u
/Sort u
when every other instance of (syntactic) Type u
/Sort u
is going to be upper case (or a greek letter / unicode identifier).
Mac (May 27 2021 at 10:45):
It feels inconsistent and symmetry breaking.
Gabriel Ebner (May 27 2021 at 10:48):
Sebastien Gouezel said:
But it's arguably a mathlib thing, so none of the concern of core.
It is, insofar as isValidAutoBoundImplicitName
cannot be overriden and rejects K
as a valid name.
Mario Carneiro (May 27 2021 at 23:00):
Mac said:
It just seems a little odd to me to auto bind lower case ASCII letters as
Type u
/Sort u
when every other instance of (syntactic)Type u
/Sort u
is going to be upper case (or a greek letter / unicode identifier).
FWIW Haskell / ML also have this split: Type parameters are lowercase letters but types and type constructors are uppercase camel-case, as in Seq a
or 'a Seq.t
Mac (May 28 2021 at 01:09):
Mario Carneiro said:
FWIW Haskell / ML also have this split: Type parameters are lowercase letters but types and type constructors are uppercase camel-case, as in
Seq a
or'a Seq.t
Yes, but, in Haskell, there is a split between the type and term namespaces. Type and term variables cannot clash or overshadow one another, making such usage more natural. As a result, one can (and many times does) have type specifications like k :: k
(ala k : k
in Lean), which is not possible in a dependently typed language like Lean.
Mac (May 28 2021 at 01:13):
In fact, Haskell makes heavy use of this split in its DataKinds
extension, where every term constructor Foo
(in the term namespace) gets an automatically promoted type constructor also named Foo
(in the type namespace).
François G. Dorais (Jun 04 2021 at 09:38):
Sebastian Ullrich said:
For efficiency and indexing reasons we might want to instead use
inductive Bool.asProp : Bool → Prop where | true : asProp true
but that would really be an implementation detail.
Is there a reason why it is not currently implemented this way? My impression is that this coercion would be easier to use than = true
, in general.
Mario Carneiro (Jun 04 2021 at 09:58):
We have a lot of tools for dealing with equality, and this inductive type is the same as what you would get by inlining the definition of Eq true
as an inductive type, so I'm not sure there is much to be gained except maybe some visual brevity (which we can also address by using unexpanders)
Mario Carneiro (Jun 04 2021 at 10:00):
in particular, the main ways to interact with this would be to prove it when it's true via ⟨⟩
and eliminate it when it's false via nomatch h
and that would work with either representation (and Eq also has another compact spelling for the first case, rfl
)
Last updated: Dec 20 2023 at 11:08 UTC