Zulip Chat Archive
Stream: lean4
Topic: New notation: using tactics in syntax
Robert Maxton (Feb 22 2024 at 21:53):
Mostly for the sake of practice with the new notation system, I'm trying to implement the syntax psuedocoded below:
Σ| WalkingPair, ⟨if_left | if_right⟩ -- => Σ (x: WalkingPair), x |> fun | left => if_left | right => if_right
Σ| (Option Bool), ⟨if_none| if_false | if_true⟩
   -- => Σ (x: Option Bool), x |> fun | none => if_none | some.false => if_false |  some.true => if_true
-- ... and so on for arbitrary finitely constructible types
So far, I've managed to come up with this:
set_option trace.Elab.definition true in
syntax (name := sigma_rcases) "Σ| " term ", ""⟨"sepBy1(ppSpace colGt term, " | ")"⟩" : term
set_option trace.Elab.definition true in
macro_rules
| `(Σ| $btype:term, ⟨$ctr_cases:term|*⟩) => do
      let fresh ← Elab.Term.mkFreshIdent btype true
      let freshRef ← `(casesTarget|$fresh:ident)
      let exacts ← ctr_cases.getElems.mapM (λ x ↦ `(tactic|exact $x))
      let binders ← ctr_cases.getElems.mapM (λ x ↦ `(rcasesPat|$(⟨mkHole x true⟩)))
      let byTactic ← `(by rcases $freshRef with ($binders|*); $exacts;*)
      `(Σ $fresh:ident, $byTactic)
I'm using by rcases to generate the constructor names I need, and assuming that the user knows what they're doing/how many blanks they need.
Unfortunately, while it type-checks, it doesn't quite work:
#check Σ x: WalkingPair, by rcases fresh with (_ | _); exact ℕ; exact ℤ
-- (x : WalkingPair) × WalkingPair.casesOn x ℕ ℤ : Type
#check (Σ| WalkingPair, ⟨ℕ | ℤ⟩)
 -- unexpected syntax
--   rcases x✝ with (_ | _)
Any advice? Also, does this belong here or under #metaprogramming / tactics ?
Robert Maxton (Mar 06 2024 at 09:01):
In case anyone runs across this in the future: the error was at
let binders ← ctr_cases.getElems.mapM (λ x ↦ `(rcasesPat|$(⟨mkHole x true⟩)))
 `(rcasesPat|... starts a parser for the parser category rcasesPat; rather than trying to synthesize the syntax, the solution was to just replace that line with 
let binders ← ctr_cases.getElems.mapM (λ _ ↦ `(rcasesPat|_))
The full solution is
open Lean Macro Parser Parser.Term Syntax Tactic
syntax "Σ| " term ", ""⟨"sepBy1(ppSpace colGt term, " | ")"⟩" : term
macro_rules
| `(Σ| $btype:term, ⟨$ctr_cases:term|*⟩) => do
      let fresh ← mkFreshIdent btype true
      let binders ← ctr_cases.getElems.mapM (λ _ ↦ `(rcasesPat|_))
      `(Σ $fresh:ident:$btype, by
          rcases $fresh:ident with ($binders|*); $[exact $ctr_cases];*)
#check (Σ| WalkingPair, ⟨ℕ | ℤ⟩) -- (x : WalkingPair) × WalkingPair.casesOn x ℕ ℤ : Type
Last updated: May 02 2025 at 03:31 UTC