Zulip Chat Archive
Stream: lean4
Topic: Yet another expression quotation package
Gabriel Ebner (May 08 2021 at 19:53):
In Lean 3, it often annoyed me that `(%%x = %%y)
didn't work. And even if it did work, you couldn't use it to implement functions such as mk_pairwise_equality : list expr → expr
(because there is no way to provide the universe level to the quotation).
Fortunately, such syntax extensions are easy to implement in Lean 4 these days:
import Quote
open Quote
def mkPairwiseEquality {α : quote Sort u} : List (quote α) → quote Prop
| [a, b] => quote a = b
| a :: b :: cs =>
let bEqCs := mkPairwiseEquality (b :: cs)
quote a = b ∧ bEqCs
| _ => quote True
Mario Carneiro (May 08 2021 at 20:56):
This has almost certainly been done before somewhere else, by somebody else.
I think there is something similar in a haskell extension (that is, a type-safe splice operator)
Gabriel Ebner (May 08 2021 at 21:12):
Mario Carneiro said:
I think there is something similar in a haskell extension (that is, a type-safe splice operator)
Thanks for the pointer, but it is indeed only similar. The Template Haskell type is Q : Type → Type
, while my type is QQ : Expr → Type
. The Type → Type
version doesn't work in Lean due to universe polymorphism: we might only know at runtime which universe the type is in, but the Type u → Type
version fixes it at compile time.
Mario Carneiro (May 08 2021 at 21:19):
There seems to be a syntactic ambiguity between the two uses of quote
for terms and types. How are they disambiguated?
Mario Carneiro (May 08 2021 at 21:20):
Also what are the actual types of quote x : quote α
and quote α : Type
?
Gabriel Ebner (May 09 2021 at 07:11):
Mario Carneiro said:
There seems to be a syntactic ambiguity between the two uses of
quote
for terms and types. How are they disambiguated?
By the expected type. Maybe it would be less confusing to use capitalization as in quote x : Quote α
.
Gabriel Ebner (May 09 2021 at 07:14):
Mario Carneiro said:
Also what are the actual types of
quote x : quote α
andquote α : Type
?
For example, quote Type : Type
expands to QQ (mkSort (mkLevelSucc levelZero))
.
And quote Nat : quote Type
expands to QQ.mk (mkConst ``Nat)
Mac (May 09 2021 at 14:42):
Mario Carneiro said:
There seems to be a syntactic ambiguity between the two uses of
quote
for terms and types. How are they disambiguated?
I am also somewhat concerned with the name clash between this quote
/Quote
and the built-in Lean quote
/Quote
for generating Syntax
.
Gabriel Ebner (May 09 2021 at 14:53):
This is literally one of the todo items:
Pick another name. There is already a
Lean.Quote
type, which means something slightly different.
Unfortunately, I only learned of Lean.Quote
after I wrote this. Suggestions for other names are welcome.
Mac (May 09 2021 at 15:12):
Gabriel Ebner said:
Unfortunately, I only learned of
Lean.Quote
after I wrote this. Suggestions for other names are welcome.
I believe @Mario Carneiro was using reflect
as his name for exhibiting a similar concept so that might fit here.
EDIT: Though now looking through the repository, I see that you are already using that terminology for untyped Expr
quotations.
Mac (May 09 2021 at 15:30):
Since you are already naming the class QQ
why not name the whole thing QQ
and the command qq
as well?
Mac (May 09 2021 at 15:31):
Alternatively, since it is a typed reflect
you could name it reflectT
/ReflectT
Mario Carneiro (May 09 2021 at 18:10):
Ever since the postfix !
on macros went away, I've had difficulty mentally parsing expressions involving macros, and this one is an example.
let bEqCs := mkPairwiseEquality (b :: cs)
quote a = b ∧ bEqCs
If I didn't know quote
was a macro, then this would be returning (quote a) = b ∧ bEqCs
, which has the wrong binding and also does not make it obvious that some major syntactic magic is happening. I would prefer that this use a notation closer to `(...)
, which already has an association to quotation in lean. Another possible way to signal the use of a macro is to have mandatory parens and no space, as in quote(a = b ∧ bEqCs)
. Since this is no longer a valid way to write application of a regular function that seems like a good candidate for "special" functions
Mario Carneiro (May 09 2021 at 18:11):
How about q!(...)
?
Gabriel Ebner (May 09 2021 at 18:19):
Yeah, it's confusing that the default precedence for macros is different from functions.
Since this is no longer a valid way to write application of a regular function that seems like a good candidate for "special" functions
Wait, what?!? You're right..
Gabriel Ebner (May 09 2021 at 18:22):
Mario Carneiro said:
How about
q!(...)
?
I think I'll go with q(...)
, since !
is no longer supposed to be used for macros.
Mario Carneiro (May 09 2021 at 18:26):
I think we should make an exception for that rule for one-letter macros to accomodate s!
and m!
Gabriel Ebner (May 09 2021 at 18:28):
Tbh, I'd prefer s"foo: {x}"
like in Scala.
Sebastian Ullrich (May 09 2021 at 18:28):
Mario Carneiro said:
I think we should make an exception for that rule for one-letter macros to accomodate
s!
andm!
https://github.com/leanprover/lean4/issues/406
Mario Carneiro (May 09 2021 at 18:28):
but if name(...)
catches on as a signal for macros then I can get behind that as well
Mario Carneiro (May 09 2021 at 18:29):
s"..."
also seems good. Generally, no space after = funny syntax is happening
Sebastian Ullrich (May 09 2021 at 18:30):
(also, https://github.com/leanprover/lean4/issues/407)
Mario Carneiro (May 09 2021 at 18:30):
also name[...]
and name{...}
(depending on the macro) a la rust
Gabriel Ebner (May 09 2021 at 18:32):
The quote a = b
was much easier to parse in the editor because quote
is highlighted as a keyword. Now with q(a = b)
, only the first q(
is highlighted. The second )
remains black. :bug:
Mario Carneiro (May 09 2021 at 18:32):
re: escaping in format strings, rust uses "{{foo}}"
, although "\{foo\}"
would also be reasonable
Mario Carneiro (May 09 2021 at 18:35):
I suppose you technically only have to escape the open brace
Mac (May 09 2021 at 19:07):
Mario Carneiro said:
I suppose you technically only have to escape the open brace
True, and "\{foo}"
looks better than "\{foo\}"
to me. Rust's "{{foo}}"
is harder to parse because it could just as easily mean interpolate {foo}
.
Mario Carneiro (May 09 2021 at 19:16):
I agree (although I think the escape \}
should still be defined and mean }
, even if it isn't required, just so people can use the other version if they prefer). Since we already have backslash-escapes in strings it seems needlessly complicated to have more than one kind of escape
Isaia Nisoli (May 09 2021 at 21:07):
Hi, I'm having some problems with the installation of lean on Ubuntu and some uncaught exception messages in leanpkg...
Isaia Nisoli (May 09 2021 at 21:08):
what is the best channel to ask help/relate bugs?
Mario Carneiro (May 09 2021 at 21:08):
which lean?
Isaia Nisoli (May 09 2021 at 21:09):
lean4
Gabriel Ebner (May 18 2021 at 10:26):
I have now also implemented pattern matching (though I had to do some creative hacks to extend the match and do notation):
import Qq
open Qq Lean
def frob : Q(Prop) → MetaM Q(Prop)
| ~q(∃ x y : α, p x y) => q(∀ x y, p y x)
| ~q(∃ x y, p x y) => q(∀ x y, p x y)
| e => throwError "{e}"
#eval frob q(∃ x y, x ≤ y + 0)
#eval frob q(∃ x : String, let x := x.length; ∃ y, x ≤ y + 42)
Gabriel Ebner (May 18 2021 at 10:27):
The syntax has changed a bit. The example at the beginning of the thread is now written as follows:
import Qq
open Qq
def mkPairwiseEquality {α : Q(Sort u)} : List Q(α) → Q(Prop)
| [a, b] => q(a = b)
| a :: b :: cs => q(a = b ∧ %(mkPairwiseEquality (b :: cs)))
| _ => q(True)
Mario Carneiro (May 18 2021 at 18:44):
Is there a reason why you have to use %(...)
instead of $(...)
for antiquotation? The latter would be better for consistency, of course
Gabriel Ebner (May 18 2021 at 18:47):
Because that would break the syntax matchers (because $(...)
is now parsed as a term).
Mario Carneiro (May 18 2021 at 18:48):
doesn't $(...)
work on any syntax class?
Gabriel Ebner (May 18 2021 at 18:49):
syntax "$(" term ")" : term
macro_rules | `($(foo) + $(bar)) => foo
/- unknown identifier 'foo' -/
Mario Carneiro (May 18 2021 at 18:49):
can you avoid the syntax "$(" term ")" : term
part?
Mario Carneiro (May 18 2021 at 18:50):
antiquotation already exists in the grammar
Mario Carneiro (May 18 2021 at 18:50):
you just need to attach a meaning to it in your macro
Gabriel Ebner (May 18 2021 at 18:51):
I get a parsing error:
#check q( $x )
-- expected '`(tactic|'
Gabriel Ebner (May 18 2021 at 18:51):
Same with $(x)
. Even if it worked, it would be really ugly to match using macro_rules.
Mario Carneiro (May 18 2021 at 18:52):
Probably, but I'd really like this to work from the user side of things
Mario Carneiro (May 18 2021 at 18:53):
The implementation of `(x)
itself might give some clues
Mac (May 18 2021 at 18:54):
The ability to use the antiquotation syntax outside of quotations was removed in a previous update: https://github.com/leanprover/lean4/commit/f0960b7f89c61ba4e28ea8239076812255b078be
Mario Carneiro (May 18 2021 at 18:55):
so in that case the q
macro just needs to use incQuotDepth
Mac (May 18 2021 at 18:55):
however, that may result in a clash with the normal antiquotation expansion
Mac (May 18 2021 at 18:56):
(especially in nested quotations)
Mario Carneiro (May 18 2021 at 18:56):
Yes, if you have mixed q(...)
and `(...)
things might get weird
Mario Carneiro (May 18 2021 at 18:56):
I will let sebastian handle the macro macro stuff, that's a bit too mind bending for me
Mario Carneiro (May 18 2021 at 18:57):
You don't need quotations to implement q(...)
, it's just a convenience, you can always just wrangle Syntax
objects directly
Mario Carneiro (May 18 2021 at 18:58):
so I would just fall back on that when the syntax gets mixed up
Gabriel Ebner (May 18 2021 at 18:59):
I'll see if I can make this incQuotDepth work. I'm not happy about the zoo of antiquotations either.
Gabriel Ebner (May 18 2021 at 18:59):
BTW, what do you think of the q(x + y)
. Would you prefer q($x + $y)
?
Mario Carneiro (May 18 2021 at 19:00):
I don't see that example above, but if x
and y
are coming from the context then I think they should be $x
and $y
Mario Carneiro (May 18 2021 at 19:01):
In short, it should act roughly like lean 3 expr quotation but with better typing
Mario Carneiro (May 18 2021 at 19:02):
Having no sigils like your original version looks nice but talking about the different meta levels is confusing as it is and syntax that is too sugary may obscure this
Gabriel Ebner (May 18 2021 at 19:05):
One motivation was that you could do q(by rwa [add_zero] at h)
but q(by rwa [add_zero] at %h)
would have been a parse error (because I don't think you can declare additional ident parsers). With the dollar, q(by rwa [add_zero] at $h)
should parse though.
Mac (May 18 2021 at 19:08):
Gabriel Ebner said:
(because I don't think you can declare additional ident parsers).
Nope, sadly. :sad: I already ran into this issue in this thread: https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Custom.20identifiers.3F/near/234409388.
Last updated: Dec 20 2023 at 11:08 UTC