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 α and quote α : 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! and m!

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