hy
hy copied to clipboard
Scope of functions resulting from a macro expansion
Would we ever want functions resulting from the expansion of an imported macro to share the macro's namespace and/or require
s? Of course a macro can be made to import
/require
its expansion's dependencies, but let's say one wanted to explicitly limit such dependencies to the resulting function's namespace/closure and not the namespace in which the macro is evaluated.
This is related to the "auto-require
s"-like functionality recently introduced in #1682, but it's not the same.
To illustrate, consider the following:
;; File test_b.hy
(defn test-fn []
(print "hi"))
;; File test_a.hy
(import [test-b [test-fn]])
(defmacro fn-create []
`(fn [] (test-fn)))
hy 0.15.0+39.gd2319dc using CPython(default) 3.6.6 on Linux
=> (require [test-a [fn-create]])
True
=> ((fn-create))
Traceback (most recent call last):
File "/home/bwillard/projects/code/python/hy-master/hy/importer.py", line 173, in hy_eval
return eval(ast_compile(expr, "<eval>", "eval"), globals, locals)
File "<eval>", line 1, in <module>
File "<eval>", line 1, in <lambda>
NameError: name 'test_fn' is not defined
In this example, or a similar one, would we ever want/expect ((fn-create))
to print "hi"
?
I think the root of this problem might be more closely aligned with the idea of macros returning function objects. If a macro could return a function object, then the macro designer could simply create a function in whatever namespace they wanted.
Currently, function objects can't be wrapped by a Hy object. Any ideas on how a function object could be represented in the Hy language model?
I don't think we can do that, because Python AST can't.
Maybe related to #919. You could sort of emulate having arbitrary Python objects in the AST using pickle. See also https://stackoverflow.com/questions/51675355/how-to-eval-a-cond-case-and-return-function-object
The easiest way for a macro to resolve to a function object defined in another namespace is probably to expand to something like __import__('foo').bar
where bar
is the function object you want that is defined in module foo
.
I'm reasonably confident that the way macro scoping works now is what we're going with for 1.0. It's what I've documented in detail in #2531.