Zulip Chat Archive

Stream: lean4

Topic: Unification and meta-variables in mkAppM'


Siddhartha Gadgil (Sep 18 2021 at 11:30):

I have been experimenting with various meta stuff using elaboration, and I do not understand why the following does not work (though many variants work). I would be grateful if this can be explained.

Firstly, stuff that works (which I use)

def self {α : Type}(a: α ) : α  := a

def selfAppM : MetaM Expr :=
  do
    let e  mkAppM' (mkConst `self) #[mkConst `Nat.zero]
    return e

#eval selfAppM

Now I try to call this with some syntax to allow input of terms (without implicit variables this works fine).

syntax (name := tryapp) term ">>>>>" term : term

@[termElab tryapp] def tryappImpl : TermElab :=
fun stx expectedType? =>
  match stx with
  | `($s >>>>> $t) =>
    do
      let f <- elabTerm s none
      let x <- elabTerm t none
      let expr : Expr   mkAppM' f #[x]
      return expr
  | _ => Elab.throwIllFormedSyntax

def one := Nat.succ >>>>> Nat.zero
#eval one  -- gives 1 as expected

However, the following does not work

#eval self >>>>> Nat.zero

giving the error

Egs2.lean:69:6
application type mismatch
  self zero
argument
  zero
has type
  Nat : Type
but is expected to have type
  ?m.2597 : Type
Egs2.lean:69:0
failed to synthesize
  MetaEval ?α

My question is why is the meta-variable not being unified with Nat? Is it that there is already an error when self needs a meta-variable, or is it that a meta-variable is generated but is of a type that is not unified?

Thanks.

Siddhartha Gadgil (Sep 18 2021 at 11:32):

I checked that if I return x after generating f but not the application, there is no error. So it seems to be unification that fails.

Leonardo de Moura (Sep 18 2021 at 14:29):

It is great to see you trying to write your own elaborators. We don't have documentation for the internal APIs yet. The elaborators that come with Lean are the best "documentation" so far.

We use themkAppM functions in the AppBuilder module for constructing auxiliary applications in meta code. For example, we use them in the simplifier for constructing funext applications, congruence proofs, etc. The mkAppM functions provide the following functionality for us: infer implicit arguments using typing constraints and type class inference. Moreover, they guarantee that no new metavariable is introduced in the resulting expression. They also do not assign metavariables that were created before we invoke mkAppM. The mkAppM functions are not elaboration functions, and they don't implement a lot of the functionality that is expected during elaboration: coercions, default parameter values, auto parameters (with tactics), etc.

Your example fails because when you elaborate s, the term @self ?m is produced, and mkAppM' will not assign the metavariable ?m since it was created before you invoked it.

In the elaborator, you can use the elabAppArgs function when you want to control the elaboration order in an application.
The file src/Lean/Elab/Extra.lean contains a few examples. In your example, you could write

@[termElab tryapp] def tryappImpl : TermElab :=
fun stx expectedType? =>
  match stx with
  | `($s >>>>> $t) =>
    do
      let f <- elabTerm s none
      let x <- elabTerm t none
      let expr  elabAppArgs f #[] #[Arg.expr x] expectedType? (explicit := false) (ellipsis := false)
      return expr
  | _ => Elab.throwIllFormedSyntax

The elabAppArgs can take named arguments, arguments that have not been elaborated yet (i.e., Syntax), etc.


Last updated: Dec 20 2023 at 11:08 UTC