reader macro #@ for with-decorator
The `with-decorator` special form is not the most ergonomic—this commit introduces a new builtin `#@` reader macro that expands to an invocation of `with-decorator`. To support this, `reader_macroexpand` is made to also look in the default `None` namespace, in imitation of how regular (non-reader) macros defined in hy.core are looked up. The docstring of `hy.macros.reader` is also edited slightly for accuracy. This in the matter of issue #856.
This commit is contained in:
parent
f247056360
commit
016557deab
@ -1463,6 +1463,25 @@ will be 4 (``1+1 + 1+1``).
|
||||
8
|
||||
|
||||
|
||||
#@
|
||||
~~
|
||||
|
||||
.. versionadded:: 0.12.0
|
||||
|
||||
The :ref:`reader macro<reader-macros>` ``#@`` can be used as a shorthand
|
||||
for ``with-decorator``. With ``#@``, the previous example becomes:
|
||||
|
||||
.. code-block:: clj
|
||||
|
||||
=> #@(inc-decorator (defn addition [a b] (+ a b)))
|
||||
=> (addition 1 1)
|
||||
4
|
||||
=> #@(inc2-decorator inc-decorator
|
||||
... (defn addition [a b] (+ a b)))
|
||||
=> (addition 1 1)
|
||||
8
|
||||
|
||||
|
||||
.. _with-gensyms:
|
||||
|
||||
with-gensyms
|
||||
|
@ -231,3 +231,9 @@
|
||||
(repeat r.text)))
|
||||
(catch [e ImportError]
|
||||
(repeat "Botsbuildbots requires `requests' to function."))))
|
||||
|
||||
|
||||
(defreader @ [expr]
|
||||
(let [[decorators (slice expr nil -1)]
|
||||
[fndef (get expr -1)]]
|
||||
`(with-decorator ~@decorators ~fndef)))
|
||||
|
21
hy/macros.py
21
hy/macros.py
@ -60,7 +60,7 @@ def macro(name):
|
||||
|
||||
|
||||
def reader(name):
|
||||
"""Decorator to define a macro called `name`.
|
||||
"""Decorator to define a reader macro called `name`.
|
||||
|
||||
This stores the macro `name` in the namespace for the module where it is
|
||||
defined.
|
||||
@ -68,7 +68,7 @@ def reader(name):
|
||||
If the module where it is defined is in `hy.core`, then the macro is stored
|
||||
in the default `None` namespace.
|
||||
|
||||
This function is called from the `defmacro` special form in the compiler.
|
||||
This function is called from the `defreader` special form in the compiler.
|
||||
|
||||
"""
|
||||
def _(fn):
|
||||
@ -176,14 +176,15 @@ def reader_macroexpand(char, tree, module_name):
|
||||
"""Expand the reader macro "char" with argument `tree`."""
|
||||
load_macros(module_name)
|
||||
|
||||
if char not in _hy_reader[module_name]:
|
||||
raise HyTypeError(
|
||||
char,
|
||||
"`{0}' is not a reader macro in module '{1}'".format(
|
||||
reader_macro = _hy_reader[module_name].get(char)
|
||||
if reader_macro is None:
|
||||
try:
|
||||
reader_macro = _hy_reader[None][char]
|
||||
except KeyError:
|
||||
raise HyTypeError(
|
||||
char,
|
||||
module_name,
|
||||
),
|
||||
)
|
||||
"`{0}' is not a defined reader macro.".format(char)
|
||||
)
|
||||
|
||||
expr = _hy_reader[module_name][char](tree)
|
||||
expr = reader_macro(tree)
|
||||
return replace_hy_obj(wrap_value(expr), tree)
|
||||
|
@ -1,3 +1,6 @@
|
||||
(import [functools [wraps]])
|
||||
|
||||
|
||||
(defn test-reader-macro []
|
||||
"Test a basic redaer macro"
|
||||
(defreader ^ [expr]
|
||||
@ -34,3 +37,34 @@
|
||||
(assert (= (, 1 2 3) a)))
|
||||
|
||||
|
||||
(defn test-builtin-decorator-reader []
|
||||
(defn increment-arguments [func]
|
||||
"Increments each argument passed to the decorated function."
|
||||
#@((wraps func)
|
||||
(defn wrapper [&rest args &kwargs kwargs]
|
||||
(apply func
|
||||
(map inc args)
|
||||
(dict-comp k (inc v) [[k v] (.items kwargs)])))))
|
||||
|
||||
#@(increment-arguments
|
||||
(defn foo [&rest args &kwargs kwargs]
|
||||
"Bar."
|
||||
(, args kwargs)))
|
||||
|
||||
;; The decorator did what it was supposed to
|
||||
(assert (= (, (, 2 3 4) {"quux" 5 "baz" 6})
|
||||
(foo 1 2 3 :quux 4 :baz 5)))
|
||||
|
||||
;; @wraps preserved the doctstring and __name__
|
||||
(assert (= "foo" (. foo --name--)))
|
||||
(assert (= "Bar." (. foo --doc--)))
|
||||
|
||||
;; We can use the #@ reader macro to apply more than one decorator
|
||||
#@(increment-arguments
|
||||
increment-arguments
|
||||
(defn double-foo [&rest args &kwargs kwargs]
|
||||
"Bar."
|
||||
(, args kwargs)))
|
||||
|
||||
(assert (= (, (, 3 4 5) {"quux" 6 "baz" 7})
|
||||
(double-foo 1 2 3 :quux 4 :baz 5))))
|
||||
|
Loading…
Reference in New Issue
Block a user