Zulip Chat Archive
Stream: general
Topic: Qq and `have`/`let`
Jovan Gerbscheid (May 10 2025 at 15:53):
Qq has this weird quirk where sometimes you have to write have instead of let, because Qq sometimes unfolds your let variables. Even though I know this I spent quite some time stuck on such a situation again recently. Is there a reason why this can't be fixed? My example is this, with an extremely confusing error message:
import Qq
open Lean Qq
def obtainExists (e : Expr) : MetaM String := do
let_expr c@Exists α p := e | failure
let u := c.constLevels![0]! -- use `have` instead of `let` to fix the error
have α : Q(Sort $u) := α
have p : Q($α → Prop) := p
withLocalDeclDQ `α α fun a => do
withLocalDeclDQ `h q($p $a) fun h => do
let proof : Q(Exists $p) := q(Exists.intro $a $h)
/-
type mismatch
Exists.intro «$a» «$h»
has type
Exists «$p» : Prop
but is expected to have type
Exists «$p» : Prop
-/
sorry
Eric Wieser (May 10 2025 at 16:14):
The rule is that let is supposed to be reducible, but Qq doesn't know what to do if you unfold to a non-Qq object
Anand Rao Tadipatri (May 10 2025 at 16:15):
Here's another way of constructing the term that avoids the have statements:
import Qq
open Lean Qq
def obtainExists (e : Expr) : MetaM String := do
let ⟨1, ~q(Prop), ~q(@Exists $α $p)⟩ ← inferTypeQ e |
throwError "Expected {e} to be of the form `Exists ..`"
withLocalDeclDQ `α α fun a => do
withLocalDeclDQ `h q($p $a) fun h => do
let proof : Q(Exists $p) := q(Exists.intro $a $h)
sorry
Jovan Gerbscheid (May 10 2025 at 16:18):
Eric Wieser said:
The rule is that
letis supposed to be reducible, but Qq doesn't know what to do if you unfold to a non-Qq object
But surely Qq could add a setting of Config.zetaDelta := false when elaborating?
Eric Wieser (May 10 2025 at 16:21):
What I mean is that this is intended behavior for let
Eric Wieser (May 10 2025 at 16:23):
With pp.all you get something a little stranger here:
type mismatch
@Exists.intro.{_private.Init.GetElem.0.List.get!Internal._uniq.1289} «$α» «$p» «$a» «$h»
has type
@Exists.{_private.Init.GetElem.0.List.get!Internal._uniq.1289} «$α» «$p» : Prop
but is expected to have type
@Exists.{_private.Init.GetElem.0.List.get!Internal._uniq.1304} «$α» «$p» : Prop
Jovan Gerbscheid (May 10 2025 at 16:24):
I doubt that it is intended behaviour that in this example the second identical line fails:
import Qq
open Lean Qq
def obtainExists (e : Expr) : MetaM String := do
let_expr c@Exists α p := e | failure
let u := c.constLevels![0]!
let α : Q(Sort $u) := α
have p : Q($α → Prop) := p
have p : Q($α → Prop) := p
sorry
Eric Wieser (May 10 2025 at 16:25):
Right, let α : Q(Sort $u) := α is not allowed because the RHS is an Expr
Eric Wieser (May 10 2025 at 16:25):
If you type-cast between Expr and Qq, you have to use have
Eric Wieser (May 10 2025 at 16:26):
Using let tells Qq it is allowed to unfold, but if it unfolds then it finds itself with no type info
Eric Wieser (May 10 2025 at 16:26):
Obviously the error message is lousy
Jovan Gerbscheid (May 10 2025 at 16:27):
But my problem is that it does work (sometimes) to use let instead of have, so for a user it is really confusing. I didn't know that you always had to use have
Jovan Gerbscheid (May 10 2025 at 16:28):
And I don't understand the andvantage of unfolding lets
Eric Wieser (May 10 2025 at 16:28):
So you can do let x : Q(Nat) := q($y + $z), and get for free that $x is defeq to $y + $z
Eric Wieser (May 10 2025 at 16:29):
I also don't understand why in your example you get away with let the first time but not the second
Last updated: Dec 20 2025 at 21:32 UTC