I have a Hy module that looks something like this:
; a.hy
(defn _add [x y]
(+ x y))
(defmacro m [x]
`(_add ~x 2))
I export part of the macro's functionality as a regular function in the module file, which is good practice as mentioned in the Hy manual. Because of the quasiquote, this does not need eval-when-compile
for the macro to work when using it in the same file. However, I would like to export the macro while hiding the function from modules that import it; this does not work as is:
; b.hy
(require a *)
(print (m 5)) ; => NameError: name '_add' is not defined
Neither eval-and-compile
nor eval-when-compile
fix this, and importing the module in addition to requiring it doesn't work either!
; b.hy
(require a *)
(import a *)
(print (m 5)) ; => NameError, still!
The only way I can make it work is making the macro import its own module to get access to _add
:
; a.hy
(defn _add [x y]
(+ x y))
(defmacro m [x]
`(do (import a [_add])
(_add ~x 2)))
This is jank, probably inconsistent (I don't know how the self-import
will behave depending on project path, though right now I just have a.hy
in the global path) and does not hide the helper function from importing modules:
; b.hy
(require a *)
(print (m 5)) ; => 7
(print (_add 5 2)) ; => 7 (expected NameError)
Is there a better way to do this?
The only way I can make it work is making the macro import its own module to get access to
_add
What you've described this way is, in fact, the right approach. Just note the difference between including an import in the expansion and importing in the macro code itself. This is the difference between (defmacro m [] `(do (import foo) …))
and (defmacro m [] (import foo) …)
.
To avoid namespace pollution, see this passage in the section you linked:
You could use
import
orrequire
to bind the module name or one of its members to a gensym, but an often more convenient option is to use the one-shot import syntaxhy.I
or the one-shot require syntaxhy.R
:(defmacro hypotenuse [a b] `(hy.I.math.sqrt (+ (** ~a 2) (** ~b 2)))) (hypotenuse 3 4)
So, you'd write
(defmacro m [x]
`(hy.I.a._add ~x 2))
Notice that you could also have _add
be a function that you call to generate code, and which you then call in m
itself instead of putting a call to it in the expansion:
; a.hy
(eval-and-compile (defn _add [x y]
`(+ ~x ~y)))
(defmacro m [x]
(_add x 2))