Zulip Chat Archive

Stream: lean4

Topic: elabTerm with class


Marcus Rossel (Feb 12 2024 at 18:26):

I don't understand the following behavior of elabTerm:

import Lean
open Lean Meta Elab Term

def f {x : Nat} : Nat := x

#eval do
  let e  elabTerm ( `(f)) none
  return e
  /-
  Lean.Expr.app (Lean.Expr.const `f []) (Lean.Expr.mvar (Lean.Name.mkNum `_uniq 1))
  -/

theorem g [Append α] (a : α) : a ++ a = a ++ a := rfl

#eval do
  let e  elabTerm ( `(g)) none
  return e
  /-
  Lean.Expr.app
  (Lean.Expr.app
    (Lean.Expr.const `g [Lean.Level.mvar (Lean.Name.mkNum `_uniq 1)])
    (Lean.Expr.mvar (Lean.Name.mkNum `_uniq 2)))
  (Lean.Expr.mvar (Lean.Name.mkNum `_uniq 3))
  -/

class C (α) where
  add       : α  α  α
  add_assoc :  a b c, add (add a b) c = add a (add b c)

#eval do
  let e  elabTerm ( `(C.add_assoc)) none
  return e
  /-
  <CoreM>:0:0: error: failed to synthesize instance
  C ?m.2
  -/

In the first two examples, Lean happily elaborates the terms and uses mvars for implicit arguments and type class arguments. Why doesn't it do so in the third example? (And is there a way I can make it behave the same as in the other examples?)

Damiano Testa (Feb 12 2024 at 20:25):

I don't know why this happens, but try adding a fake instance before the last eval:

import Lean
open Lean Meta Elab Term

class C (α) where
  add       : α  α  α
  add_assoc :  a b c, add (add a b) c = add a (add b c)

instance : C Nat := sorry

#eval do
  let e  elabTerm ( `(C.add_assoc)) none
  return e
/-
Lean.Expr.app
  (Lean.Expr.app
    (Lean.Expr.const `C.add_assoc [Lean.Level.mvar (Lean.Name.mkNum `_uniq 1)])
    (Lean.Expr.mvar (Lean.Name.mkNum `_uniq 2)))
  (Lean.Expr.mvar (Lean.Name.mkNum `_uniq 3))
-/

Damiano Testa (Feb 12 2024 at 22:04):

Here is a slightly shorter version:

import Lean.Elab.Term

class C where c : Unit

open Lean.Elab.Term

-- works always
#eval do elabTerm ( `(@C.c)) none

-- comment this instance to make the following `#eval` fail
instance : C := ⟨()⟩

-- only works if the previous instance is not commented
#eval do elabTerm ( `(C.c)) none

Eric Wieser (Feb 12 2024 at 22:08):

That last one works without the instance for me.

Eric Wieser (Feb 12 2024 at 22:09):

Oh wait, the error is being printed without a red squiggle:

<CoreM>:0:0: error: failed to synthesize instance
  C

Damiano Testa (Feb 12 2024 at 22:12):

Yes, I am very surprised by this.

Damiano Testa (Feb 12 2024 at 22:13):

It really seems to have to do with whether the environment contains some instance of the correct class.

Damiano Testa (Feb 12 2024 at 22:13):

And if you define a local instance in its section and the #eval is outside the section, then it also does not work.

Damiano Testa (Feb 12 2024 at 22:18):

And the mangled expression that elabTerm produces also contains Bool.true.

Eric Wieser (Feb 12 2024 at 22:23):

I think that's just an argument to docs#sorryAx

Damiano Testa (Feb 12 2024 at 22:26):

You are right:

#check sorryAx  -- sorryAx.{u} (α : Sort u) (synthetic : Bool := false) : α

it is probably the synthetic argument.

Damiano Testa (Feb 12 2024 at 22:28):

It seems that unless there is an instance registered, then Lean refuses to create the metavariable for the instance, unless you really ask it, by either using @ or by passing the implicit self argument with an underscore.

Damiano Testa (Feb 12 2024 at 22:29):

I find this very bizarre!

Sebastian Ullrich (Feb 12 2024 at 22:31):

You are trying to second-guess when and how Lean synthesizes synthetic metavariables, that is not a robust elaboration strategy

Damiano Testa (Feb 12 2024 at 22:44):

I am not sure whether your comment dispels or increases how mystic all this feels! :smile:


Last updated: May 02 2025 at 03:31 UTC