Zulip Chat Archive
Stream: general
Topic: chaining equalities
Kevin Buzzard (Apr 03 2022 at 17:10):
Implementation of cool maths notation :
import tactic
universe u
structure all_three_are_equal {α : Sort u} (a b c : α) : Prop :=
(hab : a = b)
(hbc : b = c)
(hac : a = c . tactic.interactive.cc)
notation a ` = ` b ` = ` c := all_three_are_equal a b c
#check 1 = 2 = 3 -- Prop
Should be fine, right? :-/
Kevin Buzzard (Apr 03 2022 at 17:13):
(My students keep wanting to do it) (hopefully next year I'll be teaching the course in Lean 4)
Trebor Huang (Apr 03 2022 at 17:25):
The next thing to do is all_four_are_equal
, right? ;)
Reid Barton (Apr 03 2022 at 17:28):
Does this now break the ordinary =
?
Kyle Miller (Apr 03 2022 at 17:50):
It's interesting how this gives you hac
for transitivity.
Something I've thought about is having Lean parse chains of binary relations x1 R1 x2 R2 x3 R3 ...
as x1 R1 x2 /\ x2 R2 x3 /\ x3 R3 x4 /\ ...
. That doesn't give you a way to get any consequences of transitivity though.
Patrick Johnson (Apr 03 2022 at 18:04):
import tactic
def eq' {α : Sort*} (s : list α) : Prop := ∀ (x y ∈ s), x = y
notation x ` = ` s:(foldr:50 ` = ` (h t, h :: t) []) := eq' (x :: s)
example : 1 + 2 = 2 + 1 = 3 :=
by { rintro x hx y hy, simp * at * }
Kevin Buzzard (Apr 03 2022 at 19:13):
#check 1 = 1 -- eq' [1, 1] : Prop
erm...
Kyle Miller (Apr 03 2022 at 19:22):
There's precedent in languages to support special constructs like this to only a particular size (like Haskell and tuple notation). Maybe this is ok:
notation x1 ` = ` x2 ` = ` x3 := x1 = x2 ∧ x2 = x3
notation x1 ` = ` x2 ` = ` x3 ` = ` x4 := x1 = x2 ∧ x2 = x3 = x4
notation x1 ` = ` x2 ` = ` x3 ` = ` x4 ` = ` x5 := x1 = x2 ∧ x2 = x3 = x4 = x5
example (x y z : ℕ) (h : x = y = z) : x = z :=
begin
-- prints as
-- h: x = y ∧ y = z
exact h.1.trans h.2,
end
Edit: This doesn't work for more than two equalities
Eric Wieser (Apr 03 2022 at 19:22):
Using docs#list.forall₂ would reduce better
Eric Wieser (Apr 03 2022 at 19:23):
(bad linkifier, https://leanprover-community.github.io/mathlib_docs/data/list/defs.html#list.forall%E2%82%82)
Eric Wieser (Apr 03 2022 at 19:23):
(deleted)
Patrick Johnson (Apr 03 2022 at 19:31):
Kevin Buzzard said:
#check 1 = 1 -- eq' [1, 1] : Prop
erm...
That's easily fixable:
import tactic
def eq' {α : Sort*} (s : list α) : Prop := ∀ (x y ∈ s), x = y
notation x ` = ` y ` = ` s:(foldr:50 ` = ` (h t, h :: t) []) := eq' (x :: y :: s)
#check 1 = 1 -- 1 = 1 : Prop
#check 1 = 1 = 1 -- eq' [1, 1, 1] : Prop
Patrick Johnson (Apr 03 2022 at 19:36):
I guess the question then is how to display eq' [1, 1, 1]
as 1 = 1 = 1
in pp. I'm not sure.
Eric Wieser (Apr 03 2022 at 19:53):
Here's the version that reduces nicely:
def eq' {α : Sort*} : list α → Prop
| [] := true
| [x] := true
| [x, y] := x = y
| (x :: y :: z :: rest) :=
list.foldr (∧) (eq' (y :: z :: rest)) (list.map ((=) x) (y :: z :: rest))
notation x ` = ` s:(foldr:50 ` = ` (h t, h :: t) []) := eq' (x :: s)
variables {w x y z : ℕ}
#reduce w = x = y = z
-- w = x ∧ w = y ∧ w = z ∧ x = y ∧ x = z ∧ y = z
Eric Wieser (Apr 03 2022 at 19:55):
Patrick Johnson said:
I guess the question then is how to display
eq' [1, 1, 1]
as1 = 1 = 1
in pp. I'm not sure.
If you remove import tactic
then pp does the right thing
Eric Wieser (Apr 03 2022 at 19:59):
In fact, import data.sym.basic
is what breaks the pretty printing
Eric Wieser (Apr 03 2022 at 20:01):
notation x ` = ` y ` = ` s:(foldr:50 ` = ` (h t, list.cons h t) []) := eq' (list.cons x $ list.cons y $ s)
fixes it, you have to disambiguate the ::
Eric Wieser (Apr 03 2022 at 20:05):
@Patrick Johnson, your definition is probably better than mine simply because it matches how docs#list.tfae is implemented (either that, or we should change tfae
)
Kevin Buzzard (Apr 03 2022 at 20:05):
Eric Wieser said:
Here's the version that reduces nicely:
def eq' {α : Sort*} : list α → Prop | [] := true | [x] := true | [x, y] := x = y | (x :: y :: z :: rest) := list.foldr (∧) (eq' (y :: z :: rest)) (list.map ((=) x) (y :: z :: rest)) notation x ` = ` s:(foldr:50 ` = ` (h t, h :: t) []) := eq' (x :: s) variables {w x y z : ℕ} #reduce w = x = y = z -- w = x ∧ w = y ∧ w = z ∧ x = y ∧ x = z ∧ y = z
Surely "nicely" := "just need w=x, x=y, y=z"?
Eric Wieser (Apr 03 2022 at 20:06):
What's nice for production tends to be less nice for consumption
Eric Wieser (Apr 03 2022 at 20:07):
Probably you want some more recursion magic to convert what you describe into the long version
Eric Wieser (Apr 03 2022 at 20:09):
I guess the version you suggest is:
def eq' {α : Sort*} : list α → Prop
| [] := true
| [x] := true
| (x :: y :: rest) := list.foldr (∧) (x = y) (y :: rest)
(untested)
Yaël Dillies (Apr 03 2022 at 20:14):
Do you not simply want list.chain' (=)
?
Eric Wieser (Apr 03 2022 at 20:16):
Eric Wieser (Apr 03 2022 at 20:17):
That's not defeq to and
Eric Wieser (Apr 03 2022 at 20:17):
Which may or may not matter
Patrick Johnson (Apr 03 2022 at 21:19):
Here is a proof that our definitions are equivalent.
Proof
Casavaca (Apr 18 2024 at 05:14):
Do we have chain equal in Lean 4? There's TFAE but that's for props.
Mario Carneiro (Apr 18 2024 at 05:16):
equalities are already all 'biconditional' so it's not nearly as involved to prove that several things are equal as it is with TFAE
Mario Carneiro (Apr 18 2024 at 05:16):
it basically comes down to a transitivity chain, and we have calc
for that
Asei Inoue (Apr 18 2024 at 09:39):
I want this chaining equation notation.
Julia provides this to us.
Alex J. Best (Apr 18 2024 at 12:36):
@Asei Inoue do you mean https://docs.julialang.org/en/v1/manual/mathematical-operations/#Chaining-comparisons, it's much more helpful if you provide a reference, not everyone knows Julia
Asei Inoue (Apr 18 2024 at 12:37):
@Alex J. Best Thanks.
Asei Inoue (Apr 19 2024 at 12:41):
Is it as difficult to implement chaining equality as it was in Lean 3?
Eric Wieser (Apr 19 2024 at 12:47):
No, it's much easier to implement it in lean4 than it was in Lean 3
Asei Inoue (Apr 19 2024 at 13:56):
No, it's much easier to implement it in lean4 than it was in Lean 3
why we don't have chaining equality notation?
Kyle Miller (Apr 19 2024 at 14:33):
One thing to think about is that x < y < z
ought to elaborate as let tmp := y; x < tmp ∧ tmp < z
so that each argument evaluates at most once. That means you probably wouldn't use it theorem statements, instead just for programming.
Asei Inoue (Apr 19 2024 at 14:38):
so that each argument evaluates at most once. That means you probably wouldn't use it theorem statements, instead just for programming.
What makes this ingenuity necessary?
Asei Inoue (Apr 19 2024 at 14:39):
What is wrong if an argument is sometimes evaluated more than once?
Kyle Miller (Apr 19 2024 at 14:39):
Every language I know of that implements chained inequalities makes sure each argument is evaluated at most once (Julia and Python come to mind)
Asei Inoue (Apr 19 2024 at 14:40):
Can the implementation in Julia be imitated in Lean?
Mario Carneiro (Apr 19 2024 at 14:43):
In most cases I think the lean compiler doesn't need this ingenuity because it does CSE much more aggressively than non-FP languages
Kyle Miller (Apr 19 2024 at 14:45):
I suppose it works with do
lifting too
Mario Carneiro (Apr 19 2024 at 14:48):
it doesn't work with dbg_trace
but I think that's because dbg_trace
is explicitly set to disable CSE
macro "chain" a:term:max b:term:max c:term:max : term => `($a < $b ∧ $b < $c)
def foo (a b : Nat) : Bool :=
chain a (dbg_trace "hi"; 5) b
#eval foo 1 7
-- hi
-- hi
-- true
Mario Carneiro (Apr 19 2024 at 14:48):
macro "chain" a:term:max b:term:max c:term:max : term => `($a < $b ∧ $b < $c)
def bar (a : Nat) := dbg_trace "hi"; a
def foo (a b : Nat) : Bool :=
chain a (bar 5) b
#eval foo 1 7
-- hi
-- true
Eric Wieser (Apr 19 2024 at 21:01):
Another trap is that (a = b) = (c = d)
already means something else; and ≤ has a similar issue
Eric Wieser (Apr 19 2024 at 21:01):
But I think this is surmountable with a clever enough elaborator
Eric Wieser (Apr 19 2024 at 21:03):
It might require cooperation with the existing elaborators
Last updated: May 02 2025 at 03:31 UTC