Zulip Chat Archive
Stream: lean4
Topic: creating proofs in `TermElabM`
Arthur Paulino (Apr 01 2022 at 22:09):
Hi! I am stuck in a challenge of the meta world.
Here you can see es
, which is of List Expression
. And then here I was able to call the Expression.app
constructor, whose second argument is a term of type NEList Expression
.
NEList α
is basically a non-empty List α
. I then used the function List.toNEList
to instantiate the term of type NEList Expression
. However, the second argument is a proof that the first argument (of type List Expression
) is not empty.
In the Parser
code it's pretty straightforward, as you can see. But I'm stuck trying to do the same in the meta world. Here I was able to create a term of type List Expression
. Now I want to use mkAppM
, calling the List.toNEList
function. But I don't know how to elaborate the expression for the second argument. Is it possible? If so, how?
Sorry if the text was too long. I hope I was able to provide enough context for my question :pray:
Simon Hudon (Apr 01 2022 at 22:34):
Many of the primitive tactics are formulated in MetaM
and work by taking a MVarId
as a parameter and may return one or many MVarId
s. You can start by creating a mvar with the right type
using Lean.Meta.mkFreshExprMVar
and then call your elaborating function that you may have written in MetaM
(and use liftMetaM
to make it compatible with TermElabM
). If you prefer writing the elaboration in TacticM
, you start the same way by creating a mvar and passing it as a parameter to Lean.Elab.Tactic.run
.
That should get you started. For more details, I would need to know what your plan is for building that proof automatically.
Arthur Paulino (Apr 01 2022 at 22:47):
The small goal is to simply be able to call the Expression.app
constructor (it requires a proof). The big goal is being able to see (and test) my interpreter with an easy way to instantiate terms of type Program
by using an unified syntax. Like this
Simon Hudon (Apr 01 2022 at 22:55):
Are you saying that my answer is not getting you there or is that a separate question?
Arthur Paulino (Apr 01 2022 at 22:56):
@Simon Hudon the issue is that, in the Parser
code, I have h
granted by the result of if h : ¬ es.isEmpty
. In the meta code, I also have if h : ¬ es.isEmpty
but es
is of type List Expr
. Whereas in the Parser, es
is of type List Expression
Arthur Paulino (Apr 01 2022 at 22:56):
(deleted)
Arthur Paulino (Apr 01 2022 at 23:00):
Simon Hudon said:
Are you saying that my answer is not getting you there or is that a separate question?
I'm saying that your answer, as I understood it, would help me building the proof in a "tactic-like" fashion. But in this scenario I would like to build a proof (as an Expr
) of the fact that my List Expression
is not empty, but all I have is the hypothesis that my List Expr
is not empty
Leonardo de Moura (Apr 01 2022 at 23:21):
We know that l
is not the empty list in your example. Then, mkListLit
produces a List.cons
application. Thus, we can prove that is not empty using reflexivty.
Another trick is to completely avoid proofs and use
def List.toNEList' (a : α) (bs : List α) : NEList α :=
match bs with
| [] => .uno a
| b::bs => .cons a (toNEList' b bs)
Here is an example. It is based on fragments of your code base.
import Lean
inductive NEList (α : Type)
| uno : α → NEList α
| cons : α → NEList α → NEList α
deriving Repr
def List.toNEList : (l : List α) → ¬ l.isEmpty → NEList α
| a :: b, _ =>
if h : ¬ b.isEmpty
then NEList.cons a $ b.toNEList h
else NEList.uno a
theorem List.notEmpty_of_eq_false (l : List α) (h : l.isEmpty = false) : ¬ l.isEmpty := by
simp [h]
def List.toNEList' (a : α) (bs : List α) : NEList α :=
match bs with
| [] => .uno a
| b::bs => .cons a (toNEList' b bs)
inductive Expression
| var : String → Expression
| app : String → NEList Expression → Expression
deriving Inhabited, Repr
declare_syntax_cat expression
syntax ident expression* : expression
syntax " ( " expression " ) " : expression
namespace Lean.Elab.Term
open Meta
def elabStringOfIdent (id : Syntax) : Expr :=
mkStrLit id.getId.toString
partial def elabExpression : Syntax → TermElabM Expr
| `(expression| $n:ident $[$es:expression]*) => do
let es ← es.data.mapM elabExpression
if h : ¬ es.isEmpty then
let l ← mkListLit (Lean.mkConst ``Expression) es -- term of type `List Expression`
let h ← mkEqRefl (← mkAppM ``List.isEmpty #[l]) -- h := (Eq.refl (List.isEmpty <l>))
let hn ← mkAppM ``List.notEmpty_of_eq_false #[l, h] -- hn := (List.notEmpty_of_eq_false <l> <h>)
let nl ← mkAppM ``List.toNEList #[l, hn] -- nl := (List.toNEList <l> <hn>)
mkAppM ``Expression.app #[elabStringOfIdent n, nl] -- app <n> <nl>
else mkAppM ``Expression.var #[elabStringOfIdent n]
| _ => throwUnsupportedSyntax
elab "#test1" e:expression:0 : command =>
Command.liftTermElabM none do
let e ← elabExpression e
check e
IO.println (← Meta.ppExpr e)
partial def elabExpression' : Syntax → TermElabM Expr
| `(expression| $n:ident $[$es:expression]*) => do
let es ← es.data.mapM elabExpression
match es with
| e::es =>
let l ← mkListLit (Lean.mkConst ``Expression) es
let nl ← mkAppM ``List.toNEList' #[e, l]
mkAppM ``Expression.app #[elabStringOfIdent n, nl]
| [] => mkAppM ``Expression.var #[elabStringOfIdent n]
| _ => throwUnsupportedSyntax
elab "#test2" e:expression:0 : command =>
Command.liftTermElabM none do
let e ← elabExpression' e
check e
IO.println (← Meta.ppExpr e)
#test2 foo a b
Arthur Paulino (Apr 01 2022 at 23:24):
Wow Leo, thank you SO MUCH for the deeply thought answer!
Leonardo de Moura (Apr 01 2022 at 23:28):
BTW, note that your parser is parsing foo a b
as foo (a b)
.
Arthur Paulino (Apr 01 2022 at 23:32):
Yeah, I'm experimenting with some tricks that should allow me to write
b := 5
a :=
x := b + 2
x
a = 7 # true
As well as
a := 2
f x y :=
z := a + x
z + y
f 3 5 # 10
Arthur Paulino (Apr 02 2022 at 03:03):
Added these just because why not
def isEq : NEList α → List α → Prop
| .cons a as, b :: bs => a = b ∧ isEq as bs
| .uno a , [b] => a = b
| _, _ => False
theorem ListToNEListIsEqList {a : α} {as : List α} :
isEq (as.toNEList a) (a :: as) := by
induction as with
| nil => rw [List.toNEList, isEq]
| cons a' as' hi =>
cases as' with
| nil => simp only [List.toNEList, isEq]
| cons _ _ =>
simp [List.toNEList, isEq] at hi ⊢
exact hi
theorem NEListToListEqList {a : α} {as : List α} :
(as.toNEList a).toList = a :: as := by
induction as with
| nil => rw [List.toNEList, NEList.toList]
| cons _ as' hi =>
cases as' with
| nil => simp only [List.toNEList, NEList.toList]
| cons _ _ =>
simp [List.toNEList, NEList.toList] at hi ⊢
exact hi
Last updated: Dec 20 2023 at 11:08 UTC