Zulip Chat Archive

Stream: general

Topic: chaining equalities


Kevin Buzzard (Apr 03 2022 at 17:10):

Implementation of cool maths notation a=b=ca=b=c:

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] as 1 = 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):

src#list.chain'

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