Zulip Chat Archive

Stream: lean4

Topic: How to get unreflected value of declaration from an attribut


Edward Ayers (Apr 03 2022 at 19:10):

I am trying to make an attribute that will run a command after a definition has been made.

def myAttributeImpl : AttributeImpl where
  name := `myattr
  descr := "my attr"
  applicationTime := AttributeApplicationTime.afterCompilation
  add decl stx kind := do
    -- assert that the type of decl is `Nat → String`
    let value  evalConst (Nat  String) decl
    dbg_trace "hello {value 4}"
    return ()

initialize registerBuiltinAttribute myAttributeImpl

@[myattr]
def myDef : Nat  String
  | x => "from myDef"

In the body of add, I want to be able to get the value safely (evalConst is unsafe) by doing some checks. What's the best way of doing this? It looks like something like this is done in KeyedDeclsAttribute but I couldn't figure it out and it looks like it only works when we have def myDef : C where C is a const.

Edward Ayers (Apr 03 2022 at 19:12):

I would quite like a solution that works with generic types, eg @[myattr] myDef1 : Nat → Bool, @[myattr] myDef2 : Nat → String, and the body of add will find the α such that value : Nat → α and then find the instance ToString α and run toString $ value 4.

Sebastian Ullrich (Apr 03 2022 at 20:19):

evalConst(Check) is the way to go, you just have to pinky-swear that you upheld its invariants by putting the call inside of https://leanprover-community.github.io/mathlib4_docs/Mathlib/Util/TermUnsafe.html

Wojciech Nawrocki (Apr 04 2022 at 14:51):

Sebastian Ullrich said:

evalConst(Check) is the way to go, you just have to pinky-swear that you upheld its invariants by putting the call inside of https://leanprover-community.github.io/mathlib4_docs/Mathlib/Util/TermUnsafe.html

How would you evalConst something that belongs to a family of types? In this case it looks like we would have to evalConst (Nat → Bool) in one case, evalConst (Nat → String) in another, etc.

Sebastian Ullrich (Apr 04 2022 at 15:09):

Ah, I was completely focused on the safety! Then you'll probably want to first compose that with toString into an Expr of type Nat -> String (or just String if you put the 4 into the term as well), put that in a new (temporary) declaration, compile it, and finally evaluate it.

Edward Ayers (Apr 04 2022 at 16:00):

@Sebastian Ullrich how you would finish implementing this:

import Lean.Meta
import Lean.Server.Rpc
import Lean.Elab.Term
open Lean Meta Elab Term System Server Tactic

def rpcAttributeImpl : AttributeImpl where
  name := `rpc
  descr := "Marks a function as a Lean server RPC method."
  applicationTime := AttributeApplicationTime.afterCompilation
  add decl stx kind := do
    let t  MetaM.run' $ TermElabM.run' do
      let a  Lean.Elab.Term.elabTerm `(registerRpcProcedure $(quote decl) _ _ $decl) none
      return a
    -- run t as a CoreM Unit here?
    return ()

Edward Ayers (Apr 04 2022 at 16:04):

Maybe the answer is "you shouldn't use attributes for this".

Edward Ayers (Apr 04 2022 at 19:55):

I've now done this as a command macro instead because I couldn't get it working in an attribute:

syntax "register_rpc" ident : command
macro_rules
  | `(register_rpc $decl) => `(#eval Lean.Server.registerRpcProcedure $(quote decl.getId) _ _ $decl )

Mario Carneiro (Apr 04 2022 at 20:10):

import Lean
import Mathlib.Util.Eval
import Mathlib.Util.TermUnsafe

open Lean Server Meta Elab Term

def myhandler (a : Nat) : RequestM (RequestTask Int) := pure $ Task.pure $ pure 0

#eval show CoreM Unit from do
  let decl := ``myhandler
  let cmd  MetaM.run' <| TermElabM.run' <| withoutModifyingEnv <| unsafe
    Mathlib.Eval.evalTerm (CoreM Unit) (mkApp (mkConst ``CoreM) (mkConst ``Unit))
      ( `(registerRpcProcedure $(quote decl) _ _ $(mkIdent decl)))
  cmd

Edward Ayers (Apr 04 2022 at 20:12):

evalTerm +1 +1 +1 +1

Edward Ayers (Apr 04 2022 at 23:37):

I got it working thanks to liftCommandElabM

import Mathlib.Tactic.Ext
open Lean

macro "register_rpc" decl:ident : command => `(#eval Lean.Server.registerRpcProcedure $(quote decl.getId) _ _ $decl )

def rpcAttributeImpl : AttributeImpl where
  name := `rpc
  descr := "Marks a function as a Lean server RPC method."
  applicationTime := AttributeApplicationTime.afterCompilation
  add decl stx kind := do
    Mathlib.Tactic.Ext.liftCommandElabM <| Elab.Command.elabCommand <|  `(register_rpc $(mkIdent decl))
    return ()

initialize registerBuiltinAttribute rpcAttributeImpl

Last updated: Dec 20 2023 at 11:08 UTC