Adding Syntax for terms
Syntax for Python for loop
We will improve upon the macro that we defined in the recipe A macro that parses Python-like for loop, for parsing Python for loop syntax. Here we use an elaborator (elab) instead of a macro to parse the same syntax. Thus, rather than just doing a simple syntactic transformation, we generate an expression from the syntax. This lets us perform more complex transformations and checks during elaboration.
This version checks whether the collection being iterated over is a List or an Array and handles each case accordingly. This also gives a more informative error message when the collection is of an unexpected type.
An elaborator that parses Python-like for loop
Here is a more robust and complete implementation using an elaborator (elab). This version checks whether the collection being iterated over is a List or an Array and handles each case accordingly:
elab "[" t:term "py_for" x:ident "in" l:term "]" :
term => do
let fnStx ← `(fun $x => $t)
let lExpr ← elabTerm l none
let fn ← elabTerm fnStx none
let ltype ← inferType lExpr
Term.synthesizeSyntheticMVarsNoPostponing
if ltype.isAppOf ``List then
mkAppM ``List.map #[fn, lExpr]
else
if ltype.isAppOf ``Array then
mkAppM ``Array.map #[fn, lExpr]
else
throwError "Expected a List or Array in py_for
comprehension, got {ltype}"
#eval [x * 2 py_for x in [1, 2, 3, 4]] --> [2, 4, 6, 8]
#eval [x * 2 py_for x in #[1, 2, 3, 4]] --> #[2, 4, 6, 8]
/--
error: Expected a List or Array in py_for
comprehension, got String
-/
#guard_msgs in
#eval [x * 2 py_for x in "List"]
Let's break down the specific metaprogramming functions used in the elaborator above:
-
Term.elabTermis used to elaborate the syntax of the collectionland the functionfnStxinto actual Lean expressions, whileMeta.inferTypeis used to determine the type of the collection. -
Term.synthesizeSyntheticMVarsNoPostponingis called to ensure that any metavariables generated during elaboration are fully resolved before we attempt to check the type. If the termlis aList,ltypewill have the formList ?m, where?mis a metavariable representing the element type. CallingTerm.synthesizeSyntheticMVarsNoPostponingensures that?mis resolved to a concrete type, allowing us to proceed with the application ofmkAppMwithout running into issues caused by unresolved metavariables. -
Expr.isAppOfis used to check whether the type oflis aListor anArray. Depending on the result, we usemkAppMto construct the appropriateList.maporArray.mapexpression. If the type is neither, we throw a custom error.