Zulip Chat Archive
Stream: mathlib4
Topic: List.destutter'
Yury G. Kudryashov (Nov 10 2024 at 05:46):
I'm looking at @Alex Meiburg 's #9082 and I wonder why docs#List.destutter' works as a special case of docs#List.destutter . I think that we'll get better lemmas if we drop a
from the answer. What do you think?
Yury G. Kudryashov (Nov 10 2024 at 05:48):
E.g., with the current definition we have
theorem tail_destutter'_congr (h : ∀ c, R a c ↔ R b c) :
(l.destutter' R a).tail = (l.destutter' R b).tail := by
and we'll be able to drop tail
if we drop the head from the output of List.destutter'
.
Yury G. Kudryashov (Nov 10 2024 at 05:49):
AFAICT, this is an auxiliary definition which is only used to prove properties of List.destutter
, so I assume that we can change its definition without worrying about backwards compatibility too much.
Eric Rodriguez (Nov 10 2024 at 23:45):
Yeah, it was much easier to develop the theory with having destutter'
around, much like docs#List.Chain'. At least, that was my experience whilst writing it.
Yury G. Kudryashov (Nov 11 2024 at 02:35):
Another question: since we don't hardcode R = \ne
, should we call it something like List.greedyChain
instead of destutter
?
Yury G. Kudryashov (Nov 11 2024 at 02:35):
It can be used, e.g., to construct monotone chains too.
Yury G. Kudryashov (Nov 11 2024 at 04:39):
Another question: should we rename docs#List.Chain' to Chain
and docs#List.Chain to something else?
Violeta Hernández (Nov 12 2024 at 03:21):
Yury G. Kudryashov said:
Another question: should we rename docs#List.Chain' to
Chain
and docs#List.Chain to something else?
That would be great, I'm tired of having to fight the docPrime linter every time I write anything involving pairwise relations...
Violeta Hernández (Nov 12 2024 at 03:23):
Maybe docs#List.Chain can be List.ChainWith
?
Yury G. Kudryashov (Nov 12 2024 at 03:23):
In fact, I don't understand why we need it at all.
Yury G. Kudryashov (Nov 12 2024 at 03:26):
We can define
def List.IsChain (R : α → α → Prop) (l : List α) : Prop :=
(l.zipWith R l.tail).Forall
or
def List.IsChain (R : α → α → Prop) (l : List α) : Prop :=
l.dropLast.Forall₂ R l.tail
Yury G. Kudryashov (Nov 12 2024 at 03:26):
Then List.Chain' R a l = List.IsChain R (a :: l)
Violeta Hernández (Nov 12 2024 at 03:32):
Well, List.Chain R a l
is already the same as List.Chain' R (a :: l)
, so its only value is being easier to prove things about
Violeta Hernández (Nov 12 2024 at 03:33):
The main difference in my experience is that docs#List.chain_cons is nicer than docs#List.chain'_cons or docs#List.chain'_cons'
Yury G. Kudryashov (Nov 12 2024 at 03:34):
It will become List.isChain_cons_cons
.
Yury G. Kudryashov (Nov 12 2024 at 03:34):
(if we drop one of 2 predicates)
Violeta Hernández (Nov 12 2024 at 03:40):
Yury G. Kudryashov said:
We can define
def List.IsChain (R : α → α → Prop) (l : List α) : Prop := (l.zipWith R l.tail).Forall
or
def List.IsChain (R : α → α → Prop) (l : List α) : Prop := l.dropLast.Forall₂ R l.tail
These re-definitions seem harder to reason about and possibly worse in terms of performance. What about something like this instead?
inductive List.IsChain (R : α → α → Prop) : List α → Prop
| nil : List.IsChain R []
| singleton (a : α) : List.IsChain R [a]
| cons_cons {a b : α} (l : List α) : R a b → List.IsChain R (b :: l) → List.IsChain R (a :: b :: l)
Violeta Hernández (Nov 12 2024 at 03:48):
Actually nevermind, performance isn't really a factor here since this is a function into Prop
and not into Bool
or anything of the sort.
Violeta Hernández (Nov 12 2024 at 03:49):
Still, I feel an inductive definition is probably going to be the easiest to work with no matter what. We can prove equivalences to the zipWith
, etc. definitions separately.
Kim Morrison (Nov 12 2024 at 03:50):
@Mario Carneiro / @François G. Dorais, any opinions here? If we're going to change the definition of Chain
I'd prefer to do to sooner rather than later.
Yury G. Kudryashov (Nov 12 2024 at 04:15):
We can prove API lemmas starting with any definition. Since it's computationally irrelevant, I don't care much about which one we use.
Yury G. Kudryashov (Nov 12 2024 at 04:15):
Possibly, an inductive one is better, because Lean won't try to unify it with something else.
François G. Dorais (Nov 12 2024 at 04:20):
Just to be sure: the two alternatives are?
Yury G. Kudryashov (Nov 12 2024 at 04:21):
I gave 2 similar definitions of List.IsChain
in terms of zipWith
, @Violeta Hernández wrote a direct inductive definition. Also, there is a status quo (Chain
and Chain'
).
Yury G. Kudryashov (Nov 12 2024 at 04:21):
I don't like status quo, because List.Chain R a l
is just List.Chain' R (a :: l)
.
François G. Dorais (Nov 12 2024 at 04:41):
There seems to be two issues:
List.Chain : α → List α → Prop
is begging for a nonempty list type as input.List.Chain': List α → Prop
is the right notion for a possibly empty list.
Violeta Hernández (Nov 12 2024 at 04:42):
There's also the less important but still relevant issue of having a primed name.
François G. Dorais (Nov 12 2024 at 04:45):
As far as the definitions of List.IsChain
, this is probably more efficient:
def List.IsChain (R : α → α → Prop) : List α → Prop
| [] | [_] => True
| x::xs@(y::_) => R x y ∧ IsChain R xs
But Violeta's inductive definition is perhaps easier to reason with.
François G. Dorais (Nov 12 2024 at 04:46):
Violeta Hernández said:
There's also the less important but still relevant issue of having a primed name.
Prime names are convenient in the short term but never good in the long term.
Violeta Hernández (Nov 12 2024 at 04:47):
François G. Dorais said:
As far as the definitions of
List.IsChain
, this is probably more efficient:def List.IsChain (R : α → α → Prop) : List α → Prop | [] | [_] => True | x::xs@(y::_) => R x y ∧ IsChain xs
But Violeta's inductive definition is perhaps easier to reason with.
I think yours might be better actually, since it makes List.IsChain.cons_cons
an iff trivially.
François G. Dorais (Nov 12 2024 at 04:48):
But "efficiency" is not so relevant unless R
is decidable.
Violeta Hernández (Nov 12 2024 at 04:50):
I think your recursive definition should also make it easier to prove decidability of IsChain
(for decidable relations)
François G. Dorais (Nov 12 2024 at 04:50):
@Kim Morrison I support replacing List.Chain
with the current List.Chain'
.
Violeta Hernández (Nov 12 2024 at 04:52):
Pinging @Bhavik Mehta since he recently added some Chain
lemmas in one of my PRs for golfing purposes
François G. Dorais (Nov 12 2024 at 04:52):
@Violeta Hernández But your inductive definition comes with an inductive principle, which is nice to have.
François G. Dorais (Nov 12 2024 at 05:13):
Note that this is actually tail recursive:
def List.isChain (r : α → α → Bool) : List α → Bool
| [] | [_] => true
| x::xs@(y::_) => r x y && isChain r xs
Yury G. Kudryashov (Nov 12 2024 at 05:16):
Is it? I mean, does Lean treat &&
so that this definition becomes tail recursive?
François G. Dorais (Nov 12 2024 at 05:17):
Yes, because &&
is short circuiting, so if r x y = false
then isChain r xs
is not evaluated.
Yury G. Kudryashov (Nov 12 2024 at 05:18):
BTW, can we somehow ask Lean whether it treats a given definition as tail recursive?
François G. Dorais (Nov 12 2024 at 05:19):
I think Mario wrote some code for that but I just inspect the IR.
François G. Dorais (Nov 12 2024 at 05:21):
set_option trace.compiler.ir.result true in
def List.isChain (r : α → α → Bool) : List α → Bool
| [] | [_] => True
| x::xs@(y::_) => r x y && isChain r xs
The key is at the end:
Bool.true →
let x_11 : u8 := List.isChain._rarg x_1 x_4;
ret x_11
François G. Dorais (Nov 12 2024 at 06:12):
Bhavik Mehta (Nov 12 2024 at 09:03):
I find List.Chain
useful as it is, I don't think it's just there to make the definition of List.Chain'
any easier. Indeed when proving things about lists, I often find myself copying the Chain/Chain' pattern in order to make the proofs go more smoothly, having support to induct on a list without changing its first element is pretty useful! So I'm in favour of keeping it, since I use List.Chain quite a bit more than I use List.Chain'. But really if we keep the induction principles that Chain currently has, phrased in terms of Chain', then I'd be happy too.
Yury G. Kudryashov (Nov 12 2024 at 16:54):
You can induct on l
in IsChain R (a :: l)
.
Yury G. Kudryashov (Nov 12 2024 at 16:54):
I'll try to make some proof-of-concept PR tonight or tomorrow.
Last updated: May 02 2025 at 03:31 UTC