Zulip Chat Archive

Stream: general

Topic: issues codegening to do notation


Frederick Pu (Oct 29 2025 at 16:46):

im trying to generate a TSyntax doSeqItem however im getting the following error:

import Lean

open Lean

def mwe (x : TSyntax `term) : MetaM (TSyntax `Lean.Parser.Term.doSeqItem) := do
  `(Lean.Parser.Term.doSeqItem| let mut x  $x)
/-
Application type mismatch: The argument
  x
has type
  TSyntax `term
but is expected to have type
  TSyntax `doElem
in the application
  x.raw
-/

there is

@[builtin_doElem_parser] def doExpr := leading_parser notFollowedByRedefinedTermToken >> termParser >> notFollowedBy (symbol ":=" <|> leftArrow) "unexpected token after 'expr' in 'do' block"

so i dont understand what the issue is

Floris van Doorn (Oct 29 2025 at 16:48):

You have to give Lean a hint that it should parse $x as a term:

import Lean

open Lean

def mwe (x : TSyntax `term) : MetaM (TSyntax `Lean.Parser.Term.doSeqItem) := do
  `(Lean.Parser.Term.doSeqItem| let mut x  $x:term)

Frederick Pu (Oct 29 2025 at 16:49):

thx!

Frederick Pu (Oct 29 2025 at 16:49):

i thought cause it was TSyntax term that that would be obvious lol

Floris van Doorn (Oct 29 2025 at 16:52):

I'm not sure whether `(...) looks at the expected type.
I've been bitten by the same issue before!

Frederick Pu (Oct 29 2025 at 16:55):

also do you see any reason why this code gen pretty prints to a metavariable instead of a do block?

import Lean
import Init
import Std.Data.HashMap.Basic
import Pullback.CImp.Syntax

open Lean
open Std

namespace CImp

abbrev ImpM := EStateM String (HashMap String Value)

def Expr.toTerm: Expr  MetaM (TSyntax `term)
  | Expr.const v => do return (quote v.toNat)
  | Expr.var x => do
    let mut q := 0
    `($(mkIdent x):ident)
  | Expr.add e₁ e₂ => do
    let v₁  Expr.toTerm e₁
    let v₂  Expr.toTerm e₂
    `($v₁ + $v₂)
  | Expr.lt e₁ e₂ => do
    let v₁  Expr.toTerm e₁
    let v₂  Expr.toTerm e₂
    `(if $v₁ < $v₂ then 1 else 0)
  | _ => `(0)
  -- | Expr.deref e => do
  --   let e' ← Expr.toTerm e
  --   -- note: the below is a valid term since it only requires the Bind typeclass instance and doesnt need do notation to work
  --   `(← Memory.read $e')

#check HashSet

def Stmt.toDoSeqItem : Stmt  StateT (HashSet Name) MetaM (TSyntaxArray `Lean.Parser.Term.doSeqItem)
  | .assign n e => do
    let xId := mkIdent n
    let eSyn  Expr.toTerm e
    if !(( get).contains n) then
      modify (fun x => x.insert n)
      pure #[ `(Lean.Parser.Term.doSeqItem| let mut $xId  $eSyn:term)]
    else
      pure #[ `(Lean.Parser.Term.doSeqItem| $xId:ident  $eSyn:term)]
  -- | .assignPtr ptr e => do
  --   let ptr' ← Expr.toTerm ptr
  --   let e' ← Expr.toTerm e
  --   pure #[← `(Lean.Parser.Term.doSeqItem| Memory.write $ptr' $e')]
  | .seq s₁ s₂ => do
    return ( toDoSeqItem s₁) ++ ( toDoSeqItem s₂)
  | .while c b => do
    let cSyn  Expr.toTerm c
    let bSyn  toDoSeqItem b
    let bSyn  `(Lean.Parser.Term.doSeqItem| while $cSyn do $bSyn*)
    pure #[bSyn]
  | .IfThenElse c t e => do
    let cSyn  Expr.toTerm c
    let tSyn  toDoSeqItem t
    let eSyn  toDoSeqItem e
    pure #[ `(Lean.Parser.Term.doSeqItem| if $cSyn then do $tSyn* else do $eSyn*)]
  | _ => pure #[]

-- Example use
def testStmt : Stmt :=
  stmt{
    while (x < 10) {
      x := x + 1;
    }
  }

#eval (do
  let (x, _)  (Stmt.toDoSeqItem testStmt).run {};
  let nihee  Lean.Elab.Term.elabTerm ( `(do $x*)) none
  IO.println "bruhh"
  IO.println ( Lean.Meta.ppExpr nihee).pretty
  : Lean.Elab.Term.TermElabM Unit)

Frederick Pu (Oct 29 2025 at 16:55):

any tips for bugging something like this?

Frederick Pu (Oct 29 2025 at 17:00):

hmm it seems to be from trying to elaboratoe the expr for x + 1 in a context where is x is not yet defined:

#eval (do
  let x  Expr.toTerm (expr{x + 0})
  let nihe  Lean.Elab.Term.elabTerm (x) none
  IO.println ( Lean.Meta.ppExpr nihe).pretty
: Lean.Elab.Term.TermElabM Unit)
-- Unknown identifier `x`

Frederick Pu (Oct 29 2025 at 17:05):

i think elabAndSynthesize is prob a better choice too:

#eval (do
  let (x, _)  (Stmt.toDoSeqItem testStmt).run {};
  IO.println ( `(do $x*)).raw
  let nihee  Lean.Elab.Term.elabTermAndSynthesize ( `(do $x*)) none
  IO.println "bruhh"
  IO.println ( Lean.Meta.ppExpr nihee).pretty
  : Lean.Elab.Term.TermElabM Unit)

Frederick Pu (Oct 29 2025 at 17:06):

im guessing i need to give it like Q(Id Unit) for the expected type or smth

Robin Arnez (Oct 29 2025 at 17:41):

You can also use Lean.logInfo nihee instead of the IO.println stuff you've done here. That alsso gives you hover information on the expression

Frederick Pu (Oct 30 2025 at 14:05):

finally works now lol:

(Term.do
 "do"
 (Term.doSeqIndent
  [(Term.doSeqItem (Term.doLetArrow "let" ["mut"] (Term.doIdDecl `x [] "←" (Term.doExpr (num "1")))) [])
   (Term.doSeqItem
    (Lean.doElemWhile_Do_
     "while"
     («term_<_» `x "<" (num "0"))
     "do"
     (Term.doSeqIndent
      [(Term.doSeqItem
        (Term.doReassignArrow (Term.doIdDecl `x [] "←" (Term.doExpr («term_+_» `x "+" (num "1")))))
        [])]))
    [])]))
bruhh
do
  let x  1
  let r 
    forIn Loop.mk x fun x r =>
        let x := r;
        if x < 0 then do
          let r  x + 1
          let x : Nat := r
          pure PUnit.unit
          pure (ForInStep.yield x)
        else pure (ForInStep.done x)
  let x : Nat := r
  pure PUnit.unit

Frederick Pu (Oct 30 2025 at 14:06):

this case for exprToTerm was returning a Nat before which was bad for some reason

| Expr.lt e₁ e₂ => do
    let v₁  Expr.toTerm e₁
    let v₂  Expr.toTerm e₂
    `($v₁ < $v₂)

Last updated: Dec 20 2025 at 21:32 UTC