Zulip Chat Archive
Stream: lean4
Topic: Qq reduceEval: failed to evaluate argument n
Kenny Lau (Aug 11 2025 at 16:00):
import Qq
open Lean Expr Qq
def Nat.mkLiteralQ (n : Nat) : Q(Nat) :=
have enlit : Q(Nat) := mkRawNatLit n
q(OfNat.ofNat $enlit)
elab "check_nat%" nStx:num : term =>
return nStx.getNat.mkLiteralQ
#check 0
#check check_nat% 0
#check 1
#check check_nat% 1
#check 37
#check check_nat% 37
namespace Qq
/-- Given `en : Q(Nat)`, return optionally a natural number `n` and a witness that `en` is defeq
to `OfNat.ofNat (natlit $n)`. -/
def natQ? (en : Q(Nat)) : Option { n : Nat // $en =Q $(n.mkLiteralQ) } :=
en.nat?.map fun n ↦ ⟨n, ⟨⟩⟩
end Qq
set_option pp.rawOnError true
/-- Interpret a given `e : Expr` as a vector `v : Fin n → Nat`. -/
def test (e : Expr) : MetaM ((n : Nat) × Q(Fin $n → Nat)) := do
let ⟨1, ~q(Fin $en → Nat), v⟩ ← inferTypeQ e
| throwError "Invalid input: cannot decompose into Fin n → Nat"
let .some n := natQ? en
| throwError "The length of the vector is not determined"
let ⟨n, _⟩ := n
let en' : Q(Nat) := n.mkLiteralQ
have v' : Q(Fin $en' → Nat) := q($v)
return ⟨n, v'⟩
Kenny Lau (Aug 11 2025 at 16:00):
I find it slightly difficult to work with Qq, because sometimes the expected type window just stops loading, and I get strange error messages
Kenny Lau (Aug 11 2025 at 16:01):
e.g. in the code above, on Line 23 the expected type window just fails with error "Error updating: Error fetching goals: Rpc error: InternalError: failed. Try again."
on Line 33 I see the same error, and then Line 34 just stops compiling. What did I do wrong?
Aaron Liu (Aug 11 2025 at 16:02):
Qq is very picky about what you let and what you have
Aaron Liu (Aug 11 2025 at 16:02):
Only let something Qq if Qq can recognize the contents
Kenny Lau (Aug 11 2025 at 16:02):
changing those lets to haves or vice versa doesn't solve anything
Kenny Lau (Aug 11 2025 at 16:03):
oh btw #check natQ? on L24 is also error
Kenny Lau (Aug 11 2025 at 16:03):
How should I fix this?
Aaron Liu (Aug 11 2025 at 16:05):
The problem is the type of natQ?
Aaron Liu (Aug 11 2025 at 16:05):
Qq can't recognize Nat.mkLiteralQ
Aaron Liu (Aug 11 2025 at 16:12):
or maybe it can't do the n in the subtype formula, based on the error message
Aaron Liu (Aug 11 2025 at 16:12):
you will have better luck doing
def natQ? (en : Q(Nat)) :
Option { n : Nat //
have k := n.mkLiteralQ
$en =Q $k } :=
Kenny Lau (Aug 11 2025 at 16:14):
yeah but then i can't use it afterwards anyway
Kenny Lau (Aug 11 2025 at 16:14):
like it seems like q($n) is just bad
Kenny Lau (Aug 11 2025 at 16:19):
import Qq
open Lean Expr Qq
def Nat.mkLiteralQ (n : Nat) : Q(Nat) :=
have enlit : Q(Nat) := mkRawNatLit n
q(OfNat.ofNat $enlit)
elab "check_nat%" nStx:num : term =>
return nStx.getNat.mkLiteralQ
#check 0
#check check_nat% 0
#check 1
#check check_nat% 1
#check 37
#check check_nat% 37
namespace Qq
/-- Given `en : Q(Nat)`, return optionally a natural number `n` and a witness that `en` is defeq
to `OfNat.ofNat (natlit $n)`. -/
def natQ? (en : Q(Nat)) : Option { n : Nat //
have en' : Q(Nat) := n.mkLiteralQ
$en =Q $en' } :=
en.nat?.map fun n ↦ ⟨n, ⟨⟩⟩
end Qq
set_option pp.rawOnError true
/-- Interpret a given `e : Expr` as a vector `v : Fin n → Nat`. -/
def test (e : Expr) : MetaM ((n : Nat) × Q(Fin $n → Nat)) := do
let ⟨1, ~q(Fin $en → Nat), v⟩ ← inferTypeQ e
| throwError "Invalid input: cannot decompose into Fin n → Nat"
let .some ⟨n, _⟩ := natQ? en
| throwError "The length of the vector is not determined"
have en' : Q(Nat) := n.mkLiteralQ
-- have v' : Q(Fin $en' → Nat) := q($v)
return ⟨n, v'⟩
Kenny Lau (Aug 11 2025 at 16:19):
This still errors
Kenny Lau (Aug 11 2025 at 16:24):
is there a better way to use Nat in Qq?
Aaron Liu (Aug 11 2025 at 16:24):
just do $n maybe
Aaron Liu (Aug 11 2025 at 16:24):
Don't know if that will work though
Kenny Lau (Aug 11 2025 at 16:25):
$n also causes errors
Kenny Lau (Aug 11 2025 at 16:25):
sometimes
Kenny Lau (Aug 11 2025 at 16:25):
it's very unpredictable (for a noob like me)
Aaron Liu (Aug 11 2025 at 16:26):
it's unpredictable for me too
Kenny Lau (Aug 11 2025 at 16:28):
mwe:
import Qq
open Lean Expr Qq
/-- Interpret a given `e : Expr` as a vector `v : Fin n → Nat`. -/
def test (e : Expr) : MetaM ((n : Nat) × Q(Fin $n → Nat)) := do
let ⟨1, ~q(Fin $en → Nat), v⟩ ← inferTypeQ e
| throwError "Invalid input: cannot decompose into Fin n → Nat"
let .some n := en.nat?
| throwError "The length of the vector is not determined"
-- have : $en =Q $n := ⟨⟩
have v' : Q(Fin $n → Nat) := q($v)
return ⟨n, v'⟩
Kenny Lau (Aug 11 2025 at 16:28):
Note that the code errors at $v (L12), and if you look at the context you'll see that it's missing the line L11 commented out above, and if you remove the comment then you get another error
Kenny Lau (Aug 11 2025 at 16:30):
the longer code is when i tried to provide API for nat?
Wuyang (Aug 11 2025 at 16:34):
working with Qq and Nat literals can be tricky... or avoiding q($n) where possible could help.
Also, you might find related tricks in LeanFinder (www.leanfinder.org).
Wuyang (Aug 11 2025 at 16:34):
@leanfinder How to correctly quote and unquote Nat values in Lean 4 Qq without type errors when using mkLiteralQ, nat?, and subtype witnesses inside macros or MetaM functions
leanfinder (Aug 11 2025 at 16:34):
Here’s what I found:
-
[definition Mathlib.Meta.NormNum.mkOfNat(α : Q(Type u)) (_sα : Q(AddMonoidWithOne $α)) (lit : Q(ℕ)) :
MetaM ((a' : Q($α)) × Q($lit = $a')) := do
if α.isConstOfNat then let a' : Q(ℕ) := q(OfNat.ofNat $lit : ℕ) pure ⟨a', (q(Eq.refl $a') : Expr)⟩ else if α.isConstOfInt then
let a' : Q(ℤ) := q(OfNat.ofNat $lit : ℤ)
pure ⟨a', (q(Eq.refl $a') : Expr)⟩
else if α.isConstOf`Rat then let a' : Q(ℚ) := q(OfNat.ofNat $lit : ℚ) pure ⟨a', (q(Eq.refl $a') : Expr)⟩ else let some n := lit.rawNatLit? | failure match n with | 0 => pure ⟨q(0 : $α), (q(Nat.cast_zero (R := $α)) : Expr)⟩ | 1 => pure ⟨q(1 : $α), (q(Nat.cast_one (R := $α)) : Expr)⟩ | k+2 => let k : Q(ℕ) := mkRawNatLit k let _x : Q(Nat.AtLeastTwo $lit) := (q(instNatAtLeastTwo (n := $k)) : Expr) let a' : Q($α) := q(OfNat.ofNat $lit) pure ⟨a', (q(Eq.refl $a') : Expr)⟩](https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Mathlib.Meta.NormNum.mkOfNat#doc) "Given a type $$\alpha$$ with anAddMonoidWithOneinstance and a natural number literallit, this function constructs an expressiona'of type $$\alpha$$ representing the canonical interpretation oflitin $$\alpha$$, along with a proof thatlitequalsa'. The function handles special cases for $$\alpha$$ beingNat,Int, orRatdirectly for efficiency, and falls back to generalNat.cast` for other types." (score: 0.700) -
definition Mathlib.Tactic.AtomM.addAtomQ{u : Level} {α : Q(Type u)} (e : Q($α)) : AtomM (Nat × {e' : Q($α) // $e =Q $e'}) := do let (n, e') ← AtomM.addAtom e return (n, ⟨e', ⟨⟩⟩) "Given an atomic expression
eof typeα, this function checks ifehas been previously encountered (up to definitional equality at the specified transparency level). If it has, it returns the index of the stored version and the stored expression (which is definitionally equal toe). If not, it storesein the list of atoms and returns its new index along witheitself. The returned expression should be considered the normal form in a normalizing tactic.
This is a strongly-typed version using Qq (quoted expressions) that ensures type safety by maintaining a proof that the returned expression e' is definitionally equal to the input e." (score: 0.695)
-
definition Mathlib.Meta.NormNum.mkRawIntLit (n : ℤ) : Q(ℤ) := let lit : Q(ℕ) := mkRawNatLit n.natAbs if 0 ≤ n then q(.ofNat $lit) else q(.negOfNat $lit) "Given an integer , this function constructs a typed expression representing in Lean's meta-programming environment, using a raw literal representation (
.lit (.natVal n)) rather than the standardOfNat.ofNatform. The function handles both non-negative integers (usingofNat) and negative integers (usingnegOfNat)." (score: 0.686) -
definition Mathlib.Notation3.natLitMatcher (n : Nat) : Matcher := fun s => do guard <| (← getExpr).rawNatLit? == n return s "The function
natLitMatchercreates a matcher for raw natural number literals in thenotation3system. Given a natural numbern, it checks whether the current expression in the match state is a raw natural number literal equal ton, and if so, returns the unchanged match state." (score: 0.684) -
definition Mathlib.Meta.NormNum.mkRawRatLit (q : ℚ) : Q(ℚ) := let nlit : Q(ℤ) := mkRawIntLit q.num let dlit : Q(ℕ) := mkRawNatLit q.den q(mkRat $nlit $dlit) "Given a rational number \( q \), this function constructs a typed expression representing \( q \) in Lean's meta-programming environment, using raw literal representations for both the numerator (as an integer) and denominator (as a natural number). The construction combines these raw literals via the
mkRatfunction to form the final rational number expression." (score: 0.684) -
definition Mathlib.Tactic.Ring.ExProd.mkNat (n : ℕ) : (e : Q($α)) × ExProd sα e := let lit : Q(ℕ) := mkRawNatLit n ⟨q(($lit).rawCast : $α), .const n none⟩ "Given a natural number
n, constructs the expression representing the constantnin the semiringα, along with its representation in theExProdtype (used for normalizing ring expressions). The resulting expression is obtained by casting the natural number literal to the semiring typeα." (score: 0.684) -
definition Mathlib.Meta.NormNum.rawIntLitNatAbs (n : Q(ℤ)) : (m : Q(ℕ)) × Q(Int.natAbs $n = $m) := if n.isAppOfArity
Int.ofNat 1 then have m : Q(ℕ) := n.appArg! ⟨m, show Q(Int.natAbs (Int.ofNat $m) = $m) from q(Int.natAbs_ofNat $m)⟩ else if n.isAppOfArityInt.negOfNat 1 then have m : Q(ℕ) := n.appArg! ⟨m, show Q(Int.natAbs (Int.negOfNat $m) = $m) from q(Int.natAbs_neg $m)⟩ else panic! "not a raw integer literal" "Given a quoted integer expressionn : Q(ℤ), this function returns a pair consisting of:
- A quoted natural number expression
m : Q(ℕ)representing the absolute value ofn - A proof that the integer absolute value of
nequalsm(i.e.,Q(Int.natAbs $$n = $$m))
The function handles both positive integers (constructed via Int.ofNat) and negative integers (constructed via Int.negOfNat), returning their absolute values with corresponding proofs." (score: 0.677)
- definition Mathlib.Meta.NormNum.Result.ofRawNat {α : Q(Type u)} (e : Q($α)) : Result e := Id.run do
let .app (.app _ (sα : Q(AddMonoidWithOne $α))) (lit : Q(ℕ)) := e | panic! "not a raw nat cast"
.isNat sα lit (q(IsNat.of_raw $α $lit) : Expr) "Given a typed expression
eof typeαthat is a raw natural number cast (i.e., of the formNat.cast n), this constructs aResult econtaining:
- The natural number literal
litbeing cast - A proof that
eis equal to the coercion oflitintoα(i.e.,IsNat e litholds)" (score: 0.676)
-
definition Qq.ofNatQ (α : Q(Type $u)) (_ : Q(Semiring $α)) (n : ℕ) : Q($α) := match n with | 0 => q(0 : $α) | 1 => q(1 : $α) | k+2 => have lit : Q(ℕ) := mkRawNatLit n have k : Q(ℕ) := mkRawNatLit k haveI : $lit =Q $k + 2 := ⟨⟩ q(OfNat.ofNat $lit) "The function converts a natural number into a term of type (represented as a quoted expression
Q($$α)), where is a type with a semiring structure. For and , it directly returns the corresponding zero and one elements of . For , it constructs the term using theOfNat.ofNatoperation with the appropriate numeral." (score: 0.674) -
definition Mathlib.Meta.Nat.unifyZeroOrSucc (n : Q(ℕ)) : MetaM (Nat.UnifyZeroOrSuccResult n) := do match ← isDefEqQ n q(0) with | .defEq pf => return .zero pf | .notDefEq => do let n' : Q(ℕ) ← mkFreshExprMVar q(ℕ) let ⟨(_pf : $n =Q Nat.succ $n')⟩ ← assertDefEqQ n q(Nat.succ $n') let (.some (n'_val : Q(ℕ))) ← getExprMVarAssignment? n'.mvarId! | throwError "could not figure out value of
?nfrom{n} =?= Nat.succ ?n" pure (.succ n'_val ⟨⟩) "Given a natural number expressionn, this function determines whethernis definitionally equal to0or can be expressed asNat.succ kfor some natural numberk. The function returns a result indicating either thatnis zero (with a proof of definitional equality) or thatnis a successor (providing the predecessork)." (score: 0.671)
Kenny Lau (Aug 11 2025 at 16:40):
I'm not sure if any of these results are relevant
Kim Morrison (Aug 12 2025 at 02:14):
Yeah, hopefully @Wuyang has learnt that this was really spammy and unhelpful to summon LeanFinder in so many places. :-(
Last updated: Dec 20 2025 at 21:32 UTC