Zulip Chat Archive
Stream: general
Topic: syntactic equality
Kevin Buzzard (May 24 2019 at 21:02):
Notation 0=has_zero.zero
-- those are syntactically equal, right? The notation is just something for the parser and pretty printer.
Chris Hughes (May 24 2019 at 21:05):
I think 0 is notation for Nat.zero
as well.
Floris van Doorn (May 24 2019 at 22:26):
There are multiple terms (which are syntactically different) that can represent the same numeral. You can see your representation if you use the option set_option pp.numerals false
:
set_option pp.numerals false example : nat.zero = 0 := _ /- not syntactically equal -/
Floris van Doorn (May 24 2019 at 22:27):
I should remark that without changing the option pp.numerals
the goal is displayed as 0 = 0
Kevin Buzzard (May 25 2019 at 07:50):
gaargh, #print notation 0
is an error because 0
is some sort of numeral. Maybe my question is: is (has_le.le 3 4) = (3 ≤ 4)
a syntactic equality?
Mario Carneiro (May 25 2019 at 08:06):
yes, assuming you haven't locally overridden <=
Mario Carneiro (May 25 2019 at 08:07):
and assuming 3 and 4 aren't being represented differently on the two sides, which can happen "in the wild"
Kevin Buzzard (May 25 2019 at 08:08):
example (n : ℕ) : n + nat.zero = n := begin rw add_zero, end example (n : ℕ) : n + has_zero.zero ℕ = n := begin rw add_zero, end
I am surprised both of these work.
Kevin Buzzard (May 25 2019 at 08:09):
example (n : ℕ) : n + nat.zero < n := begin rw add_zero, sorry end example (n : ℕ) : n + has_zero.zero ℕ < n := begin rw add_zero, sorry end
Maybe that's a better example of what I mean. I thought rw was picky about syntactic equality. Neither rewrite fails.
Mario Carneiro (May 25 2019 at 08:09):
#check bit1 (nat.succ nat.zero) -- 3 #check bit1 (has_one.one ℕ) -- 3
Kevin Buzzard (May 25 2019 at 08:10):
I understand the numerals point. I'm trying to work out when rewrites work, that's the heart of it.
Mario Carneiro (May 25 2019 at 08:10):
has_zero.zero
is reducible
Kevin Buzzard (May 25 2019 at 08:11):
But is has_zero.zero nat
syntactically equal to nat.zero
?
Mario Carneiro (May 25 2019 at 08:11):
no
Mario Carneiro (May 25 2019 at 08:12):
but it is up to unfolding reducible definitions
Kevin Buzzard (May 25 2019 at 08:12):
So does Lean unfold all reducible definitions before trying to rewrite?
Mario Carneiro (May 25 2019 at 08:12):
I think so
Kevin Buzzard (May 25 2019 at 08:12):
Or does it just internally store them as unfolded?
Mario Carneiro (May 25 2019 at 08:12):
or at least it does something equivalent to that
Mario Carneiro (May 25 2019 at 08:12):
I think it unfolds when necessary in the matching algorithm
Mario Carneiro (May 25 2019 at 08:13):
it's the same as the unification you get from exact
except it only unfolds reducible instead of reducible + semireducible
Kevin Buzzard (May 25 2019 at 08:14):
I see. A rewrite attempts to find a subterm of the goal which is "equal" to the lhs of what I'm wanting to rewrite with, and "equal" is somewhere between syntactic and definitional equality.
Mario Carneiro (May 25 2019 at 08:15):
yes
Mario Carneiro (May 25 2019 at 08:15):
yet another equality, I know
Kevin Buzzard (May 25 2019 at 08:18):
class has_scalar (α : Type u) (γ : Type v) := (smul : α → γ → γ)
(from mathlib somewhere). Why is that marked as [reducible]
?
Kevin Buzzard (May 25 2019 at 08:18):
I mean
Kevin Buzzard (May 25 2019 at 08:18):
how come has_scalar.smul
is reducible?
Kevin Buzzard (May 25 2019 at 08:19):
I don't see anyone adding the tag explicitly
Kevin Buzzard (May 25 2019 at 08:19):
but apparently the system is that notation is reducible
Kevin Buzzard (May 25 2019 at 08:19):
and I can indeed check it's reducible using #print
Kevin Buzzard (May 25 2019 at 08:20):
Are all fields of a class automatically reducible?
Mario Carneiro (May 25 2019 at 08:43):
Hm, I didn't know that. I would guess all fields are reducible by default
Kevin Buzzard (May 25 2019 at 12:16):
If z : complex
then is z.re
syntactically equal to complex.re z
?
Keeley Hoek (May 25 2019 at 12:32):
I'm pretty sure z.re
is elaborated into complex.re z
before the expression you typed is even "real"
Keeley Hoek (May 25 2019 at 12:33):
something something "projection macro"
Chris Hughes (May 25 2019 at 12:34):
I think it's actually parsed as complex.re z
, rather than elaborated.
Keeley Hoek (May 25 2019 at 12:37):
I thought the parser created a projection macro
application which said "remember to turn this into a something.re
later, you can figure out what something
means then too" and that's why if you write nonsense.foo
you get a special error in the elaborator "invalid field notation... blah"
Keeley Hoek (May 25 2019 at 12:37):
Chris Hughes (May 25 2019 at 12:39):
You're probably right. I don't know that much about this.
Chris Hughes (May 25 2019 at 12:39):
Incidentally
#eval expr.alpha_eqv `(λ z : ℂ, complex.re z) `(λ z : ℂ, z.re) -- tt
Chris Hughes (May 25 2019 at 12:40):
I'm not sure if alpha_eqv
is the same as syntactically equal.
Kevin Buzzard (May 25 2019 at 12:51):
Aah -- if I can test for syntactic equality then I don't need to bug you guys with questions :D
Keeley Hoek (May 25 2019 at 13:05):
Here is that macro in action
Keeley Hoek (May 25 2019 at 13:05):
run_cmd (do tactic.trace ``(nat.a_proj).to_raw_fmt)
Kevin Buzzard (Jan 14 2022 at 20:38):
I usually avoid talking about different kinds of equality when teaching maths undergrads, but because I'm lecturing a term-long course devoted to Lean I am going to try and tackle it. I realise I don't fully know the definition of syntactic equality. My working definition which I usually tell UGs is "literally the same keypresses in the same order". But I know there are exceptions to this. For example changing the names of bound variables doesn't destroy syntactic equality, and replacing notation by what it's notation doesn't either, I believe (because I think the parser does this before we even get to what the rest of Lean thinks; the parser might also be the reason for the bound variables thing, I have no idea about de Bruijn indices). But I think I just discovered another possibility -- perhaps changing bracket types also doesn't break syntactic equality? I'm not entirely sure how to check this. Here's an attempt, based on the maxim "rw
works up to syntactic equality":
def foo : (∀ {x : ℕ}, x = 1) ↔ (∀ (x : ℕ), x = 1) := iff.rfl
example : ∀ {x : ℕ}, x = 1 :=
begin
rw foo, -- ⊢ ∀ (x : ℕ), x = 1
rw foo, -- ⊢ ∀ (x : ℕ), x = 1
rw ← foo, -- ⊢ ∀ {x : ℕ}, x = 1
rw ← foo, -- ⊢ ∀ {x : ℕ}, x = 1
sorry,
end
Is there actually a well-defined notion of being syntactically equal or is it a slightly informal notion? If the former, does changing binder types (is that what they're called?) preserve syntactic equality?
I am living in mild fear that when I send this message I'll find a message just above it explaining everything; I couldn't figure out how to efficiently read a thread called syntactic equality other than by posting to it.
Kevin Buzzard (Jan 14 2022 at 20:40):
Aah great Chris Hughes gave me clues in 2019!
Kevin Buzzard (Jan 14 2022 at 20:42):
#eval expr.alpha_eqv `(∀ {x : ℕ}, x = 1) `(∀ (x : ℕ), x = 1) -- tt
But the above thread also teaches me that "rw
works up to syntactic equality" is not actually true?
Kevin Buzzard (Jan 14 2022 at 20:44):
#eval expr.alpha_eqv `(@has_zero.zero nat) `(nat.zero) -- ff
gaargh. Is one of "syntactically equal" and "alpha equivalent" strictly stronger than the other? I suspect I was asking about 0 because I was trying to write NNG by the way.
Rob Lewis (Jan 14 2022 at 21:04):
"literally the same keypresses in the same order" is probably not a good way to phrase this, since these concepts apply to elaborated expressions, not input (pre-) expressions. I would interpret "syntactically equal" as a judgment about the abstract syntax trees of the expressions. Think of an expr as a term of type docs#expr , so a tree of applications of those constructors. Two exprs are equal if they have identical trees. Two exprs are alpha-equivalent if their trees are the same, ignoring name and binder type arguments.
Rob Lewis (Jan 14 2022 at 21:06):
ignoring name and binder type arguments.
(I think. I actually just tried this out and it seems like binder types are identified by alpha equiv, but that's news to me.)
Kevin Buzzard (Jan 14 2022 at 21:06):
re the same keypresses -- I need a way to explain it to mathematicians and I'm absolutely not talking about expr
s, so this was a good first approximation.
Kevin Buzzard (Jan 14 2022 at 21:07):
Right -- one of the #eval
s above seemed to indicate that binder types were identified by alpha equiv assuming that's what alpha_eqv
stands for
Rob Lewis (Jan 14 2022 at 21:09):
Kevin Buzzard said:
#eval expr.alpha_eqv `(@has_zero.zero nat) `(nat.zero) -- ff
gaargh. Is one of "syntactically equal" and "alpha equivalent" strictly stronger than the other? I suspect I was asking about 0 because I was trying to write NNG by the way.
These aren't even definitionally equal -- the first is waiting for an argument of type has_zero nat
-- but even with that extra argument they're not alpha equivalent. Different syntax trees, one is an application of has_zero.zero
and the other is a constant nat.zero
.
Mario Carneiro (Jan 14 2022 at 22:21):
Kevin Buzzard said:
#eval expr.alpha_eqv `(∀ {x : ℕ}, x = 1) `(∀ (x : ℕ), x = 1) -- tt
But the above thread also teaches me that "
rw
works up to syntactic equality" is not actually true?
It's not actually true. rw
works up to defeq modulo reducible definitions, which, assuming there are no reducible definitions involved, is roughly alpha equivalence
Mario Carneiro (Jan 14 2022 at 22:24):
Kevin Buzzard said:
#eval expr.alpha_eqv `(@has_zero.zero nat) `(nat.zero) -- ff
gaargh. Is one of "syntactically equal" and "alpha equivalent" strictly stronger than the other? I suspect I was asking about 0 because I was trying to write NNG by the way.
"syntactically equal" is the most equal equal. It is equality at the meta-level, so it implies any other notion of equivalence
Mario Carneiro (Jan 14 2022 at 22:25):
In meta-lean, it means if you have a : expr
and b : expr
we're talking about a = b
Mario Carneiro (Jan 14 2022 at 22:28):
"the same keypresses in the same order" is closer to equality of pre-expressions, a = b
where a, b : pexpr
. There can be different pre-expressions that produce the same expr
after elaboration, which is why "syntactically equal" ignores certain aspects like use of notations. (Amusingly, you can also have the same pre-expression produce two exprs in two different contexts, so there is no implication in either direction here.)
Mario Carneiro (Jan 14 2022 at 22:33):
def foo (_ : ℕ → ℕ) (_ : ℤ → ℤ) : Prop := false
notation `parse_me_twice` x := foo x x
#check parse_me_twice (λ x, x)
-- foo (λ (x : ℕ), x) (λ (x : ℤ), x) : Prop
Yaël Dillies (Jan 14 2022 at 22:34):
There is potential for such weird notation!
Last updated: Dec 20 2023 at 11:08 UTC