Zulip Chat Archive

Stream: lean4

Topic: Custom errors from macro-elaborated code?


David Thrane Christiansen (Mar 18 2022 at 19:35):

I just wrote a little macro that programming-minded people might enjoy. It's to allow simple tests to be embedded in a file:

syntax "test" term:60 "=" term:60 ":" term:60 : command
syntax "test" term:60 "=" term:60 : command

macro_rules
  | `(test $x:term = $y:term : $type:term) => `(def someTest : ($x : $type) = ($y : $type) := rfl)
  | `(test $x:term = $y:term) => `(def someTest : $x = $y := rfl)

test 1 = 1 : Nat
test 1 = 1
test 1 + 1 = 2

Unfortunately , failing tests get a pretty unhelpful error message, phrased in terms of the macro's expansion to rfl:

test 1 + 1 = 3

type mismatch
   rfl
 has type
   1 + 1 = 1 + 1 : Prop
 but is expected to have type
   1 + 1 = 3 : Prop

Is there a nice way to catch and rewrite that error message? Or perhaps I need to expand to a tactic script instead, to get more control of what's going on?

David Thrane Christiansen (Mar 18 2022 at 19:37):

In particular, I'd like the error message for this example to be something like:

Expected 3, but got 2

Simon Hudon (Mar 18 2022 at 19:59):

Nice macro!

I don't think a macro will let you do that but if you use elab_rules instead of macro_rules, you can use tactic-like code and check that the two expressions are definitionally equal and throw the exact error message that you want if they're not

Simon Hudon (Mar 18 2022 at 20:00):

I'm surprised that you don't get a name clash between your test cases. I expected that the name hygiene was just for local declarations

David Thrane Christiansen (Mar 18 2022 at 20:06):

Thanks!

RE hygiene, it seems to work here, which is what the Schemer in me expected. Also, I checked whether I could refer to someTest afterwards, and I could not, which is what I'd expect with hygiene. It's quite common for Racket macros to emit helper definitions, after all, and hygiene makes this work predictably.

Do you know any code in the wild with some examples of elab_rules? As far as I can see, it is used only once in the Lean 4 repository, and that's a fairly minimal test.

Mario Carneiro (Mar 18 2022 at 20:21):

there are 6 occurrences in mathlib4

Mario Carneiro (Mar 18 2022 at 20:22):

it looks like the main use is when we want to write an elab tactic but only for some subset or special case of the actual syntax of the tactic

Arthur Paulino (Mar 18 2022 at 20:39):

You may be able to find more examples of elaboration functions in the Lean 4 source code. Organizing the code along these lines might help:

syntax (name := testStx) "#test " term:60 " = " term:60 (" : " term:60)? : command

open Lean.Elab.Command in
@[commandElab testStx] def elabTest : CommandElab := fun stx =>
  dbg_trace stx
  match stx with
  | `(#test $x:term = $y:term $[: $t:term]?) => do
    dbg_trace x
    dbg_trace y
    dbg_trace t
  | _ => Elab.throwUnsupportedSyntax

#test 1 = 1 : Nat
-- (testStx "#test" (numLit "1") "=" (numLit "1") [":" `Nat])
-- (numLit "1")
-- (numLit "1")
-- (some `Nat)

#test 1 = 2
-- (testStx "#test" (numLit "1") "=" (numLit "1") [])
-- (numLit "1")
-- (numLit "2")
-- none

Your code will run in the Lean.Elab.Command.CommandElabM monad

Arthur Paulino (Mar 18 2022 at 20:40):

You can search for functions tagged with @[commandElab ⋯] for examples

David Thrane Christiansen (Mar 18 2022 at 20:41):

Thanks, this is really helpful! I think it's time to hit the sack, but I'll revisit this next time I get a moment.

Arthur Paulino (Mar 18 2022 at 21:00):

And I forgot to mention that you can use try/catch/finally routines there. Or even throw errors with customized messages: throwError "my error message"

Leonardo de Moura (Mar 19 2022 at 00:17):

@David Thrane Christiansen Here is an example

import Lean

syntax "test" term:60 "=" term:60 : command

elab_rules : command
  | `(test $x:term = $y:term) =>
    open Lean.Elab.Command in
    open Lean.Elab.Term in
    open Lean.Meta in liftTermElabM `test do
      let x  elabTerm x none
      let y  elabTerm y none
      synthesizeSyntheticMVarsNoPostponing
      unless ( isDefEq x y) do
        throwError "Expected {y}, but got {← reduce x}"

test 1 = 1
test 1 + 1 = 2
test 1 + 1 = 3 -- error: Expected 3, but got 2

Arthur Paulino (Mar 19 2022 at 00:20):

@Leonardo de Moura It got me thinking... would it be possible to code it in the CommandElabM monad? What would be an alternative solution to elabTerm?

Leonardo de Moura (Mar 19 2022 at 00:22):

The code after => is in the CommandElabM monad, but I immediately used liftTermElabM to move to TermElabM.

Arthur Paulino (Mar 19 2022 at 00:24):

Oh, right! Thanks

Leonardo de Moura (Mar 19 2022 at 00:25):

The TermElabM tracks a lot of extra information: pending elaboration problems, pending tactics to be executed, etc.

Arthur Paulino (Mar 19 2022 at 00:31):

Is there a place from where I can learn more about these monads roles, hierarchies and transitions?
In your code you went from the CommandElabM to the TermElabM and I didn't know that was possible. And then you talked about the TermElabMonadM and I don't know where it fits in the context of this conversation

Mario Carneiro (Mar 19 2022 at 00:35):

I recall a diagram of the relation between the monads in Leo's lean 4 presentation from Lean Together 2021

Leonardo de Moura (Mar 19 2022 at 00:35):

Unfortunately, the only place right now is the Lean source code itself :( TermElabMonadM was typo. Sorry about that.

Mario Carneiro (Mar 19 2022 at 00:37):

Page 5 of https://leanprover-community.github.io/lt2021/slides/leo-LT2021-meta.pdf

Leonardo de Moura (Mar 19 2022 at 00:37):

We are currently overwhelmed. I think we will be able to greatly improve the documentation for end-users in the next few months, but we currently do not have a plan for documenting the Lean internals.

Arthur Paulino (Mar 19 2022 at 00:42):

Leonardo de Moura said:

We are currently overwhelmed. I think we will be able to greatly improve the documentation for end-users in the next few months, but we currently do not have a plan for documenting the Lean internals.

Np, I just wanted to know about already existing sources

Arthur Paulino (Mar 19 2022 at 00:43):

Mario Carneiro said:

Page 5 of https://leanprover-community.github.io/lt2021/slides/leo-LT2021-meta.pdf

So, I've seen that diagram but I don't understand it. I'll open a dedicated thread to ask questions about it

David Thrane Christiansen (Mar 19 2022 at 06:08):

This is great, thanks Leo!


Last updated: Dec 20 2023 at 11:08 UTC