Zulip Chat Archive
Stream: lean4
Topic: confusing error with quote4
Tomas Skrivan (Mar 07 2023 at 19:54):
I wanted to try out quote4 library but I'm having hard time using its antiquotations. I really do not understand why am I getting the error here:
import Qq open Qq Lean
def FinExpr :=
let n := q(10)
q(Fin $n) -- works fine
def FinExpr' : Q(Type) :=
let n := q(10)
q(Fin $n) -- unknown identifier '«$n»'
Kyle Miller (Mar 07 2023 at 20:06):
Maybe let
bindings are handled differently depending on whether there's an expected type or not (both for the whole let
and for the thing being defined)?
These work for example:
def FinExpr' : Q(Type) :=
let n : Q(Nat) := q(10)
q(Fin $n)
def FinExpr'' (n : Q(Nat)) : Q(Type) :=
q(Fin $n)
Tomas Skrivan (Mar 07 2023 at 20:09):
Odd, this also does not work
def n : Q(Nat) := q(10)
def FinExpr''' : Q(Type) := q(Fin $n)
Mario Carneiro (Mar 07 2023 at 20:11):
that's expected, the quote macro only lifts things in the local context into the inner state
Tomas Skrivan (Mar 07 2023 at 20:16):
Ok that makes sense.
Another odd thing:
This works:
#eval show MetaM Unit from do
let a? ← mkFreshExprMVar q(Nat)
let b? ← mkFreshExprMVar q(Nat)
pure ()
This does not:
#eval show MetaM Unit from do
let a? ← mkFreshExprMVarQ q(Nat)
let b? ← mkFreshExprMVarQ q(Nat) -- incompatible metavariable _uniq.146616
pure ()
What is the difference?
Mario Carneiro (Mar 07 2023 at 20:17):
One thing to watch out for is that there is a difference between QQ e
and QQ (QQ.qq e)
but these are pretty printed the same way. QQ.qq
is an identity wrapper which is used for pretty printing(?) and is generated by q(e)
expressions. When you use Q(e)
it just creates QQ e
, but if you have a function def foo (A : Q(Type)) : QQ A
then foo q(e)
will have type QQ (QQ.qq e)
Mario Carneiro (Mar 07 2023 at 20:17):
In your first example the type of FinExpr
is QQ (QQ.qq e)
and in the second one it is QQ e
Tomas Skrivan (Mar 07 2023 at 20:19):
Mario Carneiro said:
In your first example the type of
FinExpr
isQQ (QQ.qq e)
and in the second one it isQQ e
How can I see that? I set set_option pp.all false
and I still see Q(Type)
as the type of FinExpr
Mario Carneiro (Mar 07 2023 at 20:19):
you want pp.all true
Mario Carneiro (Mar 07 2023 at 20:21):
That doesn't seem to explain the difference though, you get the same result with a type ascription of QQ q(Type)
instead of Q(Type)
Mario Carneiro (Mar 07 2023 at 20:28):
BTW, when looking at Qq.QQ (@Qq.QQ.qq ...)
I am reminded of https://esolangs.org/wiki/OOo_CODE...
Gabriel Ebner (Mar 07 2023 at 20:30):
def FinExpr' : Q(Type) :=
let n := q(10)
q(Fin $n) -- unknown identifier '«$n»'
This is an unfortunate issue with elaboration order. Apparently we don't yet know the type of n
when elaborating the q(Fin $n)
, so quote4 doesn't know that it can unquote that hypothesis. We should probably postpone until the types of all assumptions are known.
Gabriel Ebner (Mar 07 2023 at 20:31):
The reason it works in the first example is presumably because we postpone to wait for the expected type.
Tomas Skrivan (Mar 07 2023 at 20:32):
How do I get this working? Even with explicit types it does not work:
#eval show MetaM Unit from do
let f? : Q(Nat→Nat) ← mkFreshExprMVar q(Nat→Nat)
let lhs : Q((Fin 10 → Nat)→Nat) := q(λ (x : Fin 10 → Nat) i => $f x i) -- unknown identifier '«$f»'
IO.println s!"{← Meta.ppExpr lhs}"
Gabriel Ebner (Mar 07 2023 at 20:33):
Tomas Skrivan said:
Odd, this also does not work
def n : Q(Nat) := q(10) def FinExpr''' : Q(Type) := q(Fin $n)
This is a bug, at the moment we assume that $n
refers to a free variable. You can write $((n))
as a workaround.
Gabriel Ebner (Mar 07 2023 at 20:34):
QQ.qq is an identity wrapper which is used for pretty printing(?)
The wrapper is used to tag an expression with a type; if you have a bare Expr
you don't know if it's supposed to denote a Nat
or something else. At some point in the past QQ
used to be a structure, and qq
was its constructor. Maybe we should get back to that.
Gabriel Ebner (Mar 07 2023 at 20:37):
Tomas Skrivan said:
How do I get this working? Even with explicit types it does not work:
#eval show MetaM Unit from do let f? : Q(Nat→Nat) ← mkFreshExprMVar q(Nat→Nat) let lhs : Q((Fin 10 → Nat)→Nat) := q(λ (x : Fin 10 → Nat) i => $f x i) -- unknown identifier '«$f»' IO.println s!"{← Meta.ppExpr lhs}"
You've got a typo here. It needs to be $f?
and not $f
. Also lhs
doesn't type-check ($f?
takes one argument, not two).
Mario Carneiro (Mar 07 2023 at 20:38):
I think if Q(e)
generated a term of the form QQ (QQ.qq e)
then there would be fewer discrepancies here
Gabriel Ebner (Mar 07 2023 at 20:41):
there is a difference between QQ e and QQ (QQ.qq e)
Does this cause any issues? If anything, it should always be QQ e
since e
is an argument of type Expr
.
Tomas Skrivan (Mar 07 2023 at 20:41):
Gabriel Ebner said:
You've got a typo here. It needs to be
$f?
and not$f
. Alsolhs
doesn't type-check ($f?
takes one argument, not two).
Good, totally my mistake. It works:
#eval show MetaM Unit from do
let f? : Q(Nat→Nat) ← mkFreshExprMVar q(Nat→Nat)
let lhs := q(λ (x : Fin 10 → Nat) i => $f? (x i))
let rhs := q(λ (x : Fin 10 → Nat) i => Nat.succ (x i))
IO.println s!"{← isDefEq lhs rhs}: {← Meta.ppExpr lhs} =?= {← Meta.ppExpr rhs}"
I'm trying to use it to test out some unification problems.
Mario Carneiro (Mar 07 2023 at 20:44):
Gabriel Ebner said:
there is a difference between QQ e and QQ (QQ.qq e)
Does this cause any issues? If anything, it should always be
QQ e
sincee
is an argument of typeExpr
.
It does; a lot of the time when I call synthInstanceQ
or other similar functions I have to write something like let inst : Q(e) <- synthInstanceQ q(e)
and I have to repeat the e
, because if I leave off the : Q(e)
then the binding gets the doubled QQ type and it won't be picked up by later q(_)
invocations
Gabriel Ebner (Mar 07 2023 at 21:38):
let inst : Q(e) <- synthInstanceQ q(e)
That pattern occurs only three times in mathlib, and in all three instances it's a workaround for the "incompatible metavariable" issue. Do you have another example?
Mario Carneiro (Mar 07 2023 at 21:44):
Yes, I noticed that as well upon searching. On the other hand, I notice that synthInstanceQ
always needs a type ascription as in synthInstanceQ (q(OrderedRing $α) : Q(Type u))
, do you know whether this is related?
Gabriel Ebner (Mar 07 2023 at 22:03):
Yes that's the incompatible metavariable issue. Quote4 expects all metavariables to have the same local context, but abstracting local variables can change the local context of an maar (at least that's what I think is happening here)
Last updated: Dec 20 2023 at 11:08 UTC