Zulip Chat Archive
Stream: std4
Topic: lint structure projections
James Gallicchio (Jul 08 2023 at 21:41):
Thoughts on a linter that disallows numerical structure projections? E.g. for e : Iff _ _
I find e.1
and e.2
much less readable than .mp
and .mpr
. I feel like the only case where I use the numbers is for tuples.
James Gallicchio (Jul 08 2023 at 21:42):
Not sure how much that syntax is used in Std/Mathlib at the moment, but it feels like bad style to me
François G. Dorais (Jul 08 2023 at 21:54):
@Mario Carneiro appears to be a fan of numerical projections. Personally, I am not a fan but I am not against it. That said, .mp
and .mpr
are pretty obscure for non-logicians (FYI: "modus ponens" and "modus ponens reverse", which should probably be .mpc
or .mpv
to avoid mixing Latin and English. This name choice goes back to Lean 2 and perhaps Lean 1.)
Mario Carneiro (Jul 08 2023 at 21:55):
yes, especially for .mp
and .mpr
I don't find the names much more than a waste of space
James Gallicchio (Jul 08 2023 at 21:56):
..really? what if you change the order of the fields in a structure? if it's just to save characters I'm not really convinced of the utility
Mario Carneiro (Jul 08 2023 at 21:56):
I disagree about renaming them to full latin though, modus ponens is the very standard name for this operation
Mario Carneiro (Jul 08 2023 at 21:56):
For iff specifically, that isn't going to happen
Mario Carneiro (Jul 08 2023 at 21:56):
for other structures, maybe not, but for And
and Iff
the field order is absolutely a stable promise
François G. Dorais (Jul 08 2023 at 21:57):
That was a joke, of course!
Mario Carneiro (Jul 08 2023 at 21:57):
also sigma and subtype
Mario Carneiro (Jul 08 2023 at 21:58):
I can't think of too many other structures where the field order is part of the primary interface
Mario Carneiro (Jul 08 2023 at 21:58):
All the basic logic operators really
James Gallicchio (Jul 08 2023 at 21:58):
For And
/Sigma
I totally see the justification, because they're tuples, and the structure names are fst
/snd
anyways. But Iff
could be written in either order, the fields aren't fst
and snd
Mario Carneiro (Jul 08 2023 at 21:58):
Iff is to be thought of as a conjunction (P -> Q) /\ (Q -> P)
Mario Carneiro (Jul 08 2023 at 21:59):
I think it was actually defined as this once
Mario Carneiro (Jul 08 2023 at 21:59):
and the order of conjuncts there is important: forward direction first, then backward
Mario Carneiro (Jul 08 2023 at 22:00):
if you changed the field order you would not only break .1
and .2
but also \<_, _\>
and every proof using constructor
Mario Carneiro (Jul 08 2023 at 22:00):
the only thing that wouldn't break is { mp := _, mpr := _ }
and literally no one writes that
James Gallicchio (Jul 08 2023 at 22:02):
I dunno. Not gonna shed any tears over it, just seems weird to me. Maybe we have some annotation on structures that should never change field order and then lint for number projections for all other structures.
Mario Carneiro (Jul 08 2023 at 22:02):
Also I think .1
on any one element structure should be okay
Mario Carneiro (Jul 08 2023 at 22:04):
Before rolling out any lint along these lines I would want data on how often it is actually violated, excepting the cases I mentioned
François G. Dorais (Jul 08 2023 at 22:31):
Mario Carneiro said:
I think it was actually defined as this once
I don't think it was but I do think it should be.
I'm pretty sure I used iff.mp
and iff.mpr
back in the Lean 2 days but I'm too lazy to go an check that far in time. (It might have been defined as a conjunction in Lean 1, I wasn't around back then.) Though it could be that these were theorems, but dot notation didn't work as well as now back then so I'm thinking they were actual projections.
In any case, since dot notation works really well by now, I think it makes perfect sense to define Iff a b
as a -> b /\ b -> a
and then add Iff.mp
and Iff.mpr
as theorems so nothing breaks downstream. The current definition is, I believe, just a historical artifact.
Mario Carneiro (Jul 08 2023 at 22:33):
those declarations definitely existed, but that's not inconsistent with the definition being a conjunction
Mario Carneiro (Jul 08 2023 at 22:34):
or.intro_left
and or.inl
both existed as well but only one is the constructor
Mario Carneiro (Jul 08 2023 at 22:35):
dot notation has worked on arbitrary declarations as long as I can remember, although it wasn't always a dot, it was e^.left
back in the day because of ambiguity with namespaced names (which is still the case, we just developed a more sophisticated system to handle the ambiguity)
Kyle Miller (Jul 08 2023 at 22:37):
I'd be wary of defining Iff
as an And
just to be sure tactics never accidentally unfold it, and also to make sure that dot notation will always refer to the Iff
namespace.
Mario Carneiro (Jul 08 2023 at 22:37):
Lean 4 has been trending towards more structures and less defs, so I think the split is deliberate
Mario Carneiro (Jul 08 2023 at 22:37):
what kyle said
Kyle Miller (Jul 08 2023 at 22:40):
More fun would be structure Iff (p q : Prop) extends And (p → q) (q → p)
:smile:
Kyle Miller (Jul 08 2023 at 22:40):
(I'm not seriously suggesting this! It's just interesting that it'd be possible, though with differences, like how .1
gives you the Iff
as an And
.)
François G. Dorais (Jul 08 2023 at 22:41):
You're totally right Mario! https://github.com/leanprover/lean2/blob/master/library/init/logic.lean#L291C3-L291C3
François G. Dorais (Jul 08 2023 at 23:32):
Going back to the Latin/English issue: it's now possible to have Iff.by
and Iff.of
! Not that I think there is any reason to change Iff.mp
and Iff.mpr
at this point or ever...
Alex Keizer (Jul 10 2023 at 10:02):
I'm also not a really big fan of having two ways to refer to the same thing (numerical and named projections). IMO, it would be nice to make the numerical projections opt-in, say with a tuple
attribute. This would serve as an explicit promise that a structure is intended to be used like a tuple, and that fields won't be re-ordered
James Gallicchio (Jul 10 2023 at 16:47):
Yeah, I'm curious how many structures would need to be opted in for mathlib to compile. I suspect constructor
/langlerangle
are pretty heavily used but maybe only on a small subset of structures.
Notification Bot (Jul 15 2023 at 16:51):
27 messages were moved from this topic to #std4 > isn't "iff" obscure? by Kyle Miller.
François G. Dorais (Jul 25 2023 at 15:11):
I've got a case where this kind of linting would be useful.
I was testing a std4 PR against Mathlib to see what would break. I found this example in Mathlib.Analysis.BoxIntegral.Basic:
have A : l.bHenstock := hl.2.1.resolve_left (by decide)
HasIntegral.of_bRiemann_eq_false_of_forall_isLittleO (hl.1.resolve_right (by decide)) B hB0 _ s hs
(fun _ => A) H₁ <| by simpa only [A, true_imp_iff] using H₂
There seems to be a coercion involved which makes resolving the issue extremely difficult. Some hidden definition has changed in my PR so that resolve_left
and resolve_right
no longer make sense. I still haven't figured it out!
Mario Carneiro (Jul 25 2023 at 17:10):
Those are almost certainly docs4#Or.resolve_left and docs4#Or.resolve_right
Kevin Buzzard (Jul 25 2023 at 17:12):
One nice Lean 4 gotcha is that in theorem Or.thing ...
you can refer to Or.resolve_left
just as resolve_left
, but if you change the name of the theorem to theorem Or_thing ...
then this no longer works.
Mario Carneiro (Jul 25 2023 at 17:13):
Does it make things clearer to add
have hl : (_ ∨ _) ∧ (_ ∨ _) ∧ _ := hl
at the top of the proof?
François G. Dorais (Jul 26 2023 at 11:36):
The issue is that the ors are no longer ors after the PR. The ands are still ands but it's not easy to figure out what they are from the relevant definition:
/-- Auxiliary equivalence with a product type used to lift an order. -/
def equivProd : IntegrationParams ≃ Bool × Boolᵒᵈ × Boolᵒᵈ where
toFun l := ⟨l.1, OrderDual.toDual l.2, OrderDual.toDual l.3⟩
invFun l := ⟨l.1, OrderDual.ofDual l.2.1, OrderDual.ofDual l.2.2⟩
left_inv _ := rfl
right_inv _ := rfl
#align box_integral.integration_params.equiv_prod BoxIntegral.IntegrationParams.equivProd
instance : PartialOrder IntegrationParams :=
PartialOrder.lift equivProd equivProd.injective
Mario Carneiro (Jul 26 2023 at 11:39):
the or is coming from src4#Bool.linearOrder
Mario Carneiro (Jul 26 2023 at 11:39):
a <= b
on bool is defined as a = false \/ b = true
apparently
Mario Carneiro (Jul 26 2023 at 11:40):
TBH I don't think that's a very good definition, a = true -> b = true
would be better
Mario Carneiro (Jul 26 2023 at 11:40):
but that's a whole refactor I'm sure
François G. Dorais (Jul 26 2023 at 11:49):
Actually, std4#183 fixes a lot of this (but not with that definition, I used leOfOrd
forward thinking to an upcoming Ord
rehash). This issue came up while attempting to make a Mathlib patch for that std4 PR.
Mario Carneiro (Jul 26 2023 at 11:51):
I figured that was why you were having porting pains
Mario Carneiro (Jul 26 2023 at 11:51):
Why do we need a LE instance on bool?
Eric Wieser (Jul 26 2023 at 11:52):
Presumably mathlib wants one so that it provide a BooleanAlgebra
instance
François G. Dorais (Jul 26 2023 at 11:52):
We at least need an Ord
instance so that deriving Ord
works properly.
Eric Wieser (Jul 26 2023 at 11:52):
But that's of course not a reason for Std4
François G. Dorais (Jul 26 2023 at 11:54):
Also Ord
should give default LE and LT as in <https://github.com/leanprover/lean4/issues/1777>
François G. Dorais (Jul 26 2023 at 11:55):
And that would mean that leOfOrd
and ltOfOrd
are the default implementations...
Mario Carneiro (Jul 26 2023 at 11:56):
actually since false < true
in rust, haskell, C, python I think it's probably good enough to have this instance in std
François G. Dorais (Jul 26 2023 at 11:57):
I think so too, but my reasoning is based on making <https://github.com/leanprover/lean4/issues/1777> work with minimal pain.
Mario Carneiro (Jul 26 2023 at 11:57):
I don't think it is necessarily a goal to have the implementations be defeq to leOfOrd
François G. Dorais (Jul 26 2023 at 11:58):
No, but then we can just delete the definition and everything will still work.
Mario Carneiro (Jul 26 2023 at 11:58):
lean4#1777 hasn't been implemented yet. Until it is we have to make do with the class we have
Mario Carneiro (Jul 26 2023 at 11:59):
which means a LawfulOrd
style class
Mario Carneiro (Jul 26 2023 at 11:59):
or rather, whatever classes exist in core/std already for lawful ordering
Mario Carneiro (Jul 26 2023 at 11:59):
and leave anything else for mathlib
François G. Dorais (Jul 26 2023 at 12:00):
I know, but it's apparently a life goal of mine to make lean4#1777 happen! :wink:
François G. Dorais (Jul 26 2023 at 12:01):
I think you need to learn the joy of programming with Ord
! :smile:
Mario Carneiro (Jul 26 2023 at 12:02):
I don't disagree with the sentiment but I don't really want to write code "as if" we had Ord when we actually don't
Mario Carneiro (Jul 26 2023 at 12:03):
in particular, in a hypothetical future with such a class there won't be any requirement that LE is derived from Ord, they will be separate fields with a compatibility theorem
François G. Dorais (Jul 26 2023 at 12:06):
Yes, but this is not a disruptive decision. It makes a lot of ugly stuff in Mathlib cleaner. The issue here is the first annoying one I ran into where it strongly relied on the definition of le
for Bool
to be an Or
. That's a bad idea anyway, so I don't think it's an issue to fix that.
Mario Carneiro (Jul 26 2023 at 12:07):
sure but changing the defeq for bool LE is a mathlib refactor that requires discussion
François G. Dorais (Jul 26 2023 at 12:08):
My patch is almost done, you can look at it tomorrow.
Mario Carneiro (Jul 26 2023 at 12:08):
I'm fairly sure that leOfOrd
doesn't give you the a = true -> b = true
definition either
François G. Dorais (Jul 26 2023 at 12:09):
No, but that is never used, and the Or
definition is only used in obscure places.
Mario Carneiro (Jul 26 2023 at 12:09):
instead you get something awkward like
(match a, b with
| false, true => .lt
| true, false => .gt
| _, _ => .eq) = .lt
François G. Dorais (Jul 26 2023 at 12:10):
On the other hand, it makes min
and max
behave normally.
François G. Dorais (Jul 26 2023 at 12:10):
You'll see the patch tomorrow when I'm done...
Mario Carneiro (Jul 26 2023 at 12:11):
if the argument is "it's fine because you shouldn't care about the defeq anyway" then it shouldn't need to be changed in the first place
Eric Wieser (Jul 26 2023 at 12:12):
Mario Carneiro said:
sure but changing the defeq for bool LE is a mathlib refactor that requires discussion
I think changing to a = true -> b = true
(aka (a = true) <= (b = true)
) would be uncontroversial, as it means le
is preserved under casting to/from bool by defeq
François G. Dorais (Jul 26 2023 at 12:14):
It's not a change, it's a move to Std. We're arguing about hypotheticals where both of us have partial info, so let's resume tomorrow after you see the patch. I don't take such decisions lightly, which is why I'm working through the patch for std#183.
Mario Carneiro (Jul 26 2023 at 12:14):
right, but François G. Dorais is changing the defeq to something else in his refactor
Mario Carneiro (Jul 26 2023 at 12:15):
I'm pretty sure the result of unfolding leOfOrd
is what I posted above
Mario Carneiro (Jul 26 2023 at 12:17):
@François G. Dorais It's not a move, AFAICT almost all of the code in std4#183 is new code, not copied from mathlib or else modified heavily such that I can't see the relation
Mario Carneiro (Jul 26 2023 at 12:18):
that's not necessarily a bad thing but it makes review a bit harder
Mario Carneiro (Jul 26 2023 at 12:18):
and to the extent that it forces mathlib to change its definitions to be consistent with std, it is a change to mathlib
Mario Carneiro (Jul 26 2023 at 12:19):
mathlib could make a definition of LE on bool which is different from the std one but this will cause the same kind of confusion as the overridden Div Int
instance in std
François G. Dorais (Jul 26 2023 at 12:23):
As I said, that's why I'm working through the Mathlib patch...
Ruben Van de Velde (Jul 26 2023 at 12:57):
I think if we're adding things to std4 that are intended to replace things in mathlib but don't match exactly, the easiest approach is to make a PR to mathlib first. Then we're sure the std4 dependency update will only require removing code
François G. Dorais (Jul 26 2023 at 14:04):
I rushed the patch and here it is: <https://github.com/leanprover-community/mathlib4/tree/fgdorais-bool>
It will take an hour or so for the CI to get done. It compiles fine on my machine but I haven't run linters and such yet. Look at the next to last commit, the last commit is just to point std to the branch std4#183 is on. For some reason that I can't recall at the moment, I included the patch for std#184 in that one commit. It's not hard tell which is which, but it makes the patch uselessly bigger since std4#184 will be obsoleted by std4#200.
As you can see the changes are mostly deletions. It also nicely cleans up the mess discussed on Zulip a long while ago. There was exactly one file in Mathlib that relied on le
for Bool
being an Or
, and that was fixed nicely using lemmas that were already in Mathlib.
So, like I said before, all we need is more information. There is no harm to Mathlib nor Std, in fact, there are benefits to both!
François G. Dorais (Jul 26 2023 at 14:07):
Actually, this uses a slightly outdated version of std4#183 (that's the price of rushing it...) but it should work fine with the more recent version except for xor
which should just become an export Bool (xor)
in Mathlib.Init.Bool.Basic.
François G. Dorais (Jul 26 2023 at 14:14):
Oh bummer, compilation is fine but it looks like there's a bunch of simp redundancies that were found by the simp linter... I don't intend to get around to that since std4#200 is more important for now.
Last updated: Dec 20 2023 at 11:08 UTC