我有一个 Hy 模块,看起来像这样:
; a.hy
(defn _add [x y]
(+ x y))
(defmacro m [x]
`(_add ~x 2))
我将宏的部分功能导出为模块文件中的常规函数,这是 Hy 手册中提到的良好实践。由于准引用,因此在使用宏时不需要
eval-when-compile
宏即可工作在同一个文件中。但是,我想导出宏,同时对导入它的模块隐藏该函数;这不能按原样工作:
; b.hy
(require a *)
(print (m 5)) ; => NameError: name '_add' is not defined
eval-and-compile
和eval-when-compile
都无法解决此问题,并且除了要求模块之外导入模块也不起作用!
; b.hy
(require a *)
(import a *)
(print (m 5)) ; => NameError, still!
我可以让它工作的唯一方法是让宏导入自己的模块以访问
_add
:
; a.hy
(defn _add [x y]
(+ x y))
(defmacro m [x]
`(do (import a [_add])
(_add ~x 2)))
这是jank,可能不一致(我不知道self-
import
将如何根据项目路径表现,尽管现在我在全局路径中只有a.hy
)并且not隐藏导入模块的辅助函数:
; b.hy
(require a *)
(print (m 5)) ; => 7
(print (_add 5 2)) ; => 7 (expected NameError)
有更好的方法吗?
我可以让它工作的唯一方法是让宏导入自己的模块以访问
_add
您所描述的这种方式实际上是正确的做法。只需注意在生成代码中包含导入和在宏代码本身中导入之间的区别即可。这就是
(defmacro m [] (import foo) …)
和(defmacro m [] `(do (import foo) …))
之间的区别。另请注意,我在手册中推荐的良好实践是提取生成代码的子例程,而不是在扩展中调用的子例程。
为了避免名称空间污染,请参阅您链接的部分中的这段文字:
您可以使用
或import
将模块名称或其成员之一绑定到 gensym,但通常更方便的选择是使用一次性导入语法require
或一次性 require 语法hy.I
:hy.R
(defmacro hypotenuse [a b] `(hy.I.math.sqrt (+ (** ~a 2) (** ~b 2)))) (hypotenuse 3 4)
所以,你会写
(defmacro m [x]
`(hy.I.a._add ~x 2))