Zulip Chat Archive

Stream: lean4

Topic: Generating `Expr` terms by quoting


Anand Rao (Mar 22 2022 at 17:28):

I have some code operating on terms of the Expr type. I would like to test this with a few examples to see if it is working as intended, but I am facing some difficulty in creating Expr terms.

In Lean3, I could wrap a term e around using a back-quote with parentheses (like this - `(e)) to generate the expression corresponding to e. However, trying the same in Lean4 gives me a complicated term with the Syntax type.

Is there an easy way to convert a term to its corresponding Expr in Lean4? Thanks in advance.

Jannis Limperg (Mar 22 2022 at 17:35):

Gabriel's quotation library is probably relevant: https://github.com/gebner/quote4

With builtin Lean 4 tools, use elabTerm to elaborate the Syntax you get out of the quotation.

Anand Rao (Mar 22 2022 at 18:09):

Thank you.

I am having a bit of trouble specifying the expected type to the elabTerm function, since it requires the expected type to be quoted as an expression:

import Lean
open Lean
open Expr
open Elab

/-
If the expression is of the form
A₁ → A₂ → A₃ → … → Aₙ → … → B,
the output of `target` is `B`.

Essentially, it recovers the term at the end of a series of nested implications.
-/
def target : Expr  Expr
  | lam n t b d => target b
  | forallE n t b d => target b
  | e => e

#check Term.elabTerm

example : TermElabM Expr := do
  let s := `(λ (x : Nat) => x + 2)
  let (e : Expr)  Term.elabTerm s (Option.some sorry)
  return (target e)

Specifically, I do not know how to replace the sorry above with Nat -> Nat (the expected type) quoted as an expression.

Siddhartha Gadgil (Mar 23 2022 at 00:19):

You can use ← mkArrow (mkConst ``Nat) (mkConst ``Nat) to get Nat -> Nat. You may need to open Meta.

Siddhartha Gadgil (Mar 23 2022 at 00:54):

There is another issue with let s. Here is a version with the two corrections.

def target : Expr  Expr
  | Expr.lam n t b d => target b
  | Expr.forallE n t b d => target b
  | e => e

#check Term.elabTerm

example : TermElabM Expr := do
  let s   `(λ (x : Nat) => x + 2)
  let (e : Expr)  Term.elabTerm s (Option.some ( mkArrow (mkConst ``Nat) (mkConst ``Nat)))
  return (target e)

Siddhartha Gadgil (Mar 23 2022 at 01:00):

Another thing: if you are pattern matching on expressions, you should first reduce them (and often take whnf). For instance, you can compare with and without the additional line with reduce in the following.

def target : Expr  Expr
  | Expr.lam n t b d => target b
  | Expr.forallE n t b d => target b
  | e => e

#check Term.elabTerm

def tst : TermElabM Expr := do
  let s   `(λ (x : Nat) => x + 2)
  let (e : Expr)  Term.elabTerm s (Option.some ( mkArrow (mkConst ``Nat) (mkConst ``Nat)))
  let e   reduce e
  return (target e)

#eval tst

Siddhartha Gadgil (Mar 23 2022 at 01:24):

Incidentally, a trick I use to test this by getting a term is to write a small elaborator. In this case it shows a bug (this is code after the above).

elab "tst!" : term => do
  tst

#eval tst!

However I assume this was just an exercise with syntax. If you try to compute the target this way, another issue is that you will have bound variables in the expression, which have no meaning (or the wrong meaning due to de Bruijn indexing) outside their context and so will give an error.

Anand Rao (Mar 23 2022 at 03:56):

Thank you very much. It turns out that with this fix to the let expression, the elabTermalso accepts Option.none as its second argument (presumably because it has now managed to infer the type of the expression s correctly). I see why my usage of := instead of <- in that example was incorrect.

Anand Rao (Mar 23 2022 at 04:09):

Thank you for pointing out the issue with the bound variables. In my actual use case (which is to write a small tactic in Lean4), I will be trying to extract the target type (and not the target). Most of my inputs will not be dependent types, so I hope the issues with bound variables do not arise.

Jannis Limperg (Mar 23 2022 at 09:23):

Anand Rao said:

Most of my inputs will not be dependent types, so I hope the issues with bound variables do not arise.

Note that even just polymorphism requires you to handle dependencies: your target does not work on forall α, List α -> List α. So it's unlikely that you can avoid this complication. You should probably be using forallTelescope or one of its variants.


Last updated: Dec 20 2023 at 11:08 UTC