macropy
macropy copied to clipboard
Named arguments for macros
My kwargs hack related to issue #13, mainly for discussion for now.
In this version:
- New magic is added to
**kw
, calledkwargs
. -
kwargs
gets the AST, as alist
ofast.keyword
objects, of named arguments passed to the macro invocation. - As a result of this change, full expr-macro invocation syntax (from normal run-time code) is now
mac(a0, ..., an, k0=v0, ..., km=vm)[body]
; thekj=vj
pairs are new. Similarly for block and decorator macros (just the placement ofbody
differs).
The named args are kept completely separate from the MacroPy **kw
arguments, to protect MacroPy internals, and to prevent shadowing (in either direction). This captures only the user-given named args (e.g. x=1
) in kwargs.
This complete separation is very important for the use of kwargs as syntax for binding constructs. Otherwise, e.g. a let
construct might try to bind the name gen_sym
(similarly for other MacroPy internals) because it happens to have an entry in **kw
.
IMHO, the already existing args and the proposed kwargs together form one complete feature, namely the use of all the possibilities offered by Python's function call syntax, to provide extra slots for ASTs to be fed into the macro invocation beside the single body.
Also IMHO, this fixes an asymmetry in the MacroPy API. Already, macros can be given positional args, but no named args from the use site. (The existing implementation silently ignores named args, which I think is just an oversight; it should at least raise an error if we don't want to support named args. But that's really a separate issue.)
The body can of course already be a tuple, or really anything, so the whole feature does not add any fundamental capability - but it provides some flexibility for the syntax macros can offer to the user. For example, let constructs look more readable with a syntactically separate bindings block, which naturally takes one of the forms let((x, 1), (y, 2))[body]
or let(x=1, y=2)[body]
. I think the latter is more pythonic.
Thoughts?
Tests? :P or later, after the discussion has been resolved? it just seems worth ensuring that existing implementation silently ignores named args, which I think is just an oversight
is not true anymore
Ah, very good point. I did my testing in the context of unpythonic
, so I sort of forgot to add tests for this to MacroPy itself.
Yes, after discussion is the perfect time to add tests.
So I'm going soon to publish a package for macropy, @Technologicat do you care to add a test or two for this?
Sure! I'll add some tests and push them, probably later today.
Done, test added. It gets picked up by run_tests.py
, and passes in Python 3.6.
While at it, I made the mechanism a bit more user-friendly: since the keyword arg name is always a string constant, the value is now automatically extracted from the keyword
AST node.
The way the user macro implementation sees it, the parameter kwargs
is now a dict of {argname_as_str: ast_node}
. As mentioned before, it includes only the named args from the macro call site. Any MacroPy implicit args such as gen_sym
and stop
work exactly as before.
This separation is important when the kwargs
feature is used for creating custom binding forms (e.g. let
), and also to avoid clobbering the MacroPy implicit args if the user happens to name an arg e.g. as stop
.
Inside the implementation, I changed the name kwargs
to call_kwargs
to match the existing naming scheme for call_args
.
I'm not sure if the extrakws
field is actually needed or not. It seems it's always empty (what gets passed in MacroPy's magic **kw
is extrakws
, plus the filevars that contain all the MacroPy implicit args). However, I did just a local analysis, so I may have missed something.
Anything else? :)
Ping? Anything that still remains to be done?