宏通过其定义生成函数实现?

Macro to generate function implementation by its definition?

是否可以自动编写远程函数的实现?

如果是手写的话,它看起来像下面的代码。同时拥有函数声明和实现很重要。

proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
  echo (fn, a, b)

proc multiply(a, b: int): int

proc multiply(a, b: int): int =
  rcall("multiply", a, b, int)

我想把它自动化写成

proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
  echo (fn, a, b)

proc multiply(a, b: int): int

remotefn multiply

并且 remotefn 宏应该查看函数定义并将其实现生成为 rcall("multiply", a, b, int),这可能吗?

Would it be possible to automate writing the implementation ...

是的,使用带有 typed 参数(然后是 getTypeImpl

的 nim 宏,可以自动执行几乎任何事情
import std/macros

macro dumpImpl(arg: typed): untyped =
  echo arg.getTypeImpl().treeRepr()
  
  
proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
  echo (fn, a, b)

proc multiply(a, b: int): int

proc multiply(a, b: int): int =
  rcall("multiply", a, b, int)
  
  
dumpImpl multiply

表明multiply类型(函数签名)具有以下结构:

ProcTy
  FormalParams
    Sym "int"
    IdentDefs
      Sym "a"
      Sym "int"
      Empty
    IdentDefs
      Sym "b"
      Sym "int"
      Empty
  Empty

虽然重要的是要记住重载过程不能仅根据名称轻松解决(因为,好吧,有 许多 实现)。最明显的选择是仍然使用 typed 参数,但传递一些参数来消除函数调用的歧义

import std/macros

macro dumpImpl(arg: typed): untyped =
  echo arg.treeRepr()
  
  
proc overload(a: string) = discard
proc overload(a: int) = discard


dumpImpl overload
# ClosedSymChoice - lists all possible overloads with their respective symbols
#   Sym "overload"
#   Sym "overload"

dumpImpl overload("123")
#Call
#  Sym "overload"
#  StrLit "123"

dumpImpl overload(123)
#Call
#  Sym "overload"
#  IntLit 123

作为一个小的(个人)旁注——当你谈论 nim 宏时,问题应该不是“如果这可能吗?”而是“执行此操作的最佳方法是什么?”。它可能需要一些宏技巧的知识,但几乎可以实现任何东西。

EDIT1(添加实现代码示例,在评论中回复问题):

import std/[macros]

proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
  echo (fn, a, b)

macro remotefn(fn: typed) =
  let fname = fn.str_val()
  # `quote do` generates hygienic identifiers - i.e. all new
  # variables/functions introduced by it are unique and not visible in
  # global. To fix this you need to explicitly create identifier for your
  # procedure using `ident`

  let
    multId = ident("multiply") # < Function name identifier

    # Same goes for variables - nim does have a name-based overloading, so
    # you need to make sure function arguments use the same identifiers as
    # the original one
    aId = ident("a")
    bId = ident("b")

  result = quote do:
    proc `multId`(`aId`, `bId`: int): int =
      rcall(`fname`, 1, 1, int)

  echo result.toStrLit()

proc multiply(a, b: int): int
remotefn multiply