Zulip Chat Archive
Stream: mathlib4
Topic: Set notation
Dan Velleman (Nov 19 2022 at 19:58):
I thought it would be good to be able to write definitions like this:
def inv {A B : Type} (R : Set (A × B)) : Set (B × A) :=
{(b, a) : B × A | (a, b) ∈ R}
But this doesn't fit the pattern of set theory notation defined in Mathlib.Init.Set.lean. So I tried this:
syntax "{ " term " : " term " | " term " }" : term
macro_rules
| `({ $pat:term : $t | $p }) => `(setOf fun x : $t => match x with | $pat => $p)
This seems to work, but then it creates problems for other set theory notation. Something like {x : Nat | x = 0}
becomes ambiguous--I assume that's because it fits both the previous syntax pattern and the new one I have defined. Is there a way to eliminate the ambiguity?
Thomas Murrills (Nov 19 2022 at 20:40):
Allowing destructuring seems useful! Does it still work (as-is, not in a way that fixes the ambiguity) if you just use fun $pat : $t => $p
on the rhs, since iirc fun
allows destructuring as well? If so, maybe the ambiguity could be resolved by replacing the existing macro (which in this case would only differ by restricting $pat
to be an ident
).
I think the trick, though, would be to first check if $pat
was a valid destructuring for the given type, and if not, throw-unsupported and allow the low-priority extBinders
case to take over. (But I’m not sure how to do the first part, and maybe there’s a reason that all of this wouldn’t be a good idea that I haven’t thought of.)
Thomas Murrills (Nov 19 2022 at 20:53):
Though, thinking about it, maybe it’s Std.Util.ExtendedBinder
that should allow destructuring in general instead of just ident
s? Not sure how much this would break or how feasible it would be, though…
Dan Velleman (Nov 20 2022 at 16:48):
Perhaps what I need is this:
elab "{ " pat:term " : " t:term " | " p:term " }" : term => do
if pat.raw.isIdent then
Lean.Elab.throwUnsupportedSyntax
else
Lean.Elab.Term.elabTerm (← `(setOf fun x : $t => match x with | $pat => $p)) none
This avoids creating a second possible interpretation for something like {x : Nat | x = 0}
.
But would it make sense to add something like this to the Mathlib definition of set notation?
Dan Velleman (Nov 20 2022 at 17:24):
On the other hand, this isn't good enough:
elab "{ " pat:term " | " p:term " }" : term => do
if pat.raw.isIdent then
Lean.Elab.throwUnsupportedSyntax
else
Lean.Elab.Term.elabTerm (← `(setOf fun x => match x with | $pat => $p)) none
This causes {x > 0 | x < 10}
to be ambiguous. I suppose a more complicated test could be used to avoid the ambiguity.
Is the binderPred
notation really needed for anything? I'd rather have the destructuring notation than the binderPred
notation.
Dan Velleman (Nov 23 2022 at 17:55):
After a lot of experimentation, here's what I've come up with:
elab "{ " pat:term " : " t:term " | " p:term " }" : term => do
if pat.raw.isIdent then
Lean.Elab.throwUnsupportedSyntax --To avoid creating ambiguity with existing set notation
else
Lean.Elab.Term.elabTerm (← `(setOf fun x : $t => match x with | $pat => $p)) none
declare_syntax_cat identBinderPred
syntax ident binderPred : identBinderPred
elab "{ " pat:(identBinderPred <|> term) " | " p:term " }" : term => do
match pat.raw with
| .ident .. => Lean.Elab.throwUnsupportedSyntax --To avoid ambiguity
| .node _ `identBinderPred__ _ => Lean.Elab.throwUnsupportedSyntax --To avoid ambiguity
| _ =>
let patTerm : Lean.Term := ⟨pat⟩
Lean.Elab.Term.elabTerm (← `(setOf fun x => match x with | $patTerm => $p)) none
@[app_unexpander setOf]
def setOf.unexpander : Lean.PrettyPrinter.Unexpander
| `($_ fun $_:ident =>
match $_:ident with
| $pat => $p) => `({ $pat:term | $p })
| _ => throw ()
I don't understand all of this, but it seems to work. It allows me to write things like {(m, n) : Nat × Nat | m < n}
Is there any reason not to do this? Is there a better way to do it?
Last updated: Dec 20 2023 at 11:08 UTC