diff --git a/NEWS b/NEWS index 582544b..f175ae8 100644 --- a/NEWS +++ b/NEWS @@ -23,7 +23,7 @@ Changes from 0.13.0 * `return` has been implemented as a special form * `while` loops may now contain an `else` clause, like `for` loops * `xi` from `hy.extra.anaphoric` is now the `#%` tag macro - * `#%` also has a new `&kwargs` parameter `%**`. + * `#%` works on any expression and has a new `&kwargs` parameter `%**` [ Bug Fixes ] * Numeric literals are no longer parsed as symbols when followed by a dot diff --git a/docs/extra/anaphoric.rst b/docs/extra/anaphoric.rst index 1e4d062..a630eae 100644 --- a/docs/extra/anaphoric.rst +++ b/docs/extra/anaphoric.rst @@ -234,20 +234,32 @@ Returns a function which applies several forms in series from left to right. The #% == -Usage ``#%(body ...)`` +Usage ``#% expr`` -Makes a function with an implicit parameter list from ``%`` parameters. +Makes an expression into a function with an implicit ``%`` parameter list. -A ``%i`` symbol designates the `i` th parameter (1-based, e.g. ``%1 %2 %3`` etc.). +A ``%i`` symbol designates the (1-based) *i* th parameter (such as ``%3``). +Only the maximum ``%i`` determines the number of ``%i`` parameters--the +others need not appear in the expression. ``%*`` and ``%**`` name the ``&rest`` and ``&kwargs`` parameters, respectively. -Nesting of ``#%()`` forms is not recommended. - -This is similar to Clojure's anonymous function literals (``#()``). .. code-block:: hy - => (#%(identity [%1 %5 [%2 %3] %* %4]) 1 2 3 4 5 6 7 8) - [1, 5, [2, 3,] (6, 7, 8), 4] - => (def add-10 #%(+ 10 %1)) - => (add-10 6) - 16 + => (#%[%1 %6 42 [%2 %3] %* %4] 1 2 3 4 555 6 7 8) + [1, 6, 42, [2, 3], (7, 8), 4] + => (#% %** :foo 2) + {"foo": 2} + +When used on an s-expression, +``#%`` is similar to Clojure's anonymous function literals--``#()``. + +.. code-block:: hy + + => (setv add-10 #%(+ 10 %1)) + => (add-10 6) + 16 + +``#%`` determines the parameter list by the presence of a ``%*`` or ``%**`` +symbol and by the maximum ``%i`` symbol found *anywhere* in the expression, +so nesting of ``#%`` forms is not recommended. + diff --git a/hy/extra/anaphoric.hy b/hy/extra/anaphoric.hy index 73dc5fb..9ad92b7 100644 --- a/hy/extra/anaphoric.hy +++ b/hy/extra/anaphoric.hy @@ -5,7 +5,6 @@ ;;; These macros make writing functional programs more concise - (defmacro ap-if [test-form then-form &optional else-form] `(do (setv it ~test-form) @@ -112,29 +111,32 @@ "Returns a function which is the composition of several forms." `(fn [var] (ap-pipe var ~@forms))) -(deftag % [body] - "makes a function with an implicit parameter list from `%` parameters. +(deftag % [expr] + "Makes an expression into a function with an implicit `%` parameter list. - A %i symbol designates the ith parameter (1-based, e.g. `%1 %2 %3` etc.). + A `%i` symbol designates the (1-based) ith parameter (such as `%3`). + Only the maximum `%i` determines the number of `%i` parameters--the + others need not appear in the expression. `%*` and `%**` name the `&rest` and `&kwargs` parameters, respectively. - Nesting of `#%()` forms is not recommended." - (setv flatbody (flatten body)) - `(fn [;; generate all %i symbols up to the maximum found in body - ~@(genexpr (HySymbol (+ "%" - (str i))) - [i (range 1 - ;; find the maximum %i - (-> (list-comp (int (cut a 1)) - [a flatbody] - (and (symbol? a) - (.startswith a '%) - (.isdigit (cut a 1)))) - (+ [0]) - max - inc))]) - ;; generate the &rest parameter only if '%* is present in body - ~@(if (in '%* flatbody) + + Nesting of `#%` forms is not recommended." + (setv %symbols (set-comp a + [a (flatten [expr])] + (and (symbol? a) + (.startswith a '%)))) + `(fn [;; generate all %i symbols up to the maximum found in expr + ~@(genexpr (HySymbol (+ "%" (str i))) + [i (range 1 (-> (list-comp (int (cut a 1)) + [a %symbols] + (.isdigit (cut a 1))) + (or (, 0)) + max + inc))]) + ;; generate the &rest parameter only if '%* is present in expr + ~@(if (in '%* %symbols) '(&rest %*)) - ~@(if (in '%** flatbody) + ;; similarly for &kwargs and %** + ~@(if (in '%** %symbols) '(&kwargs %**))] - (~@body))) + ~expr)) + diff --git a/tests/native_tests/extra/anaphoric.hy b/tests/native_tests/extra/anaphoric.hy index e0ab038..76bf635 100644 --- a/tests/native_tests/extra/anaphoric.hy +++ b/tests/native_tests/extra/anaphoric.hy @@ -127,4 +127,18 @@ ;; test &rest &kwargs (assert-equal (#%(, %* %**) 1 2 :a 'b) (, (, 1 2) - (dict :a 'b)))) + (dict :a 'b))) + ;; test other expression types + (assert-equal (#% %* 1 2 3) + (, 1 2 3)) + (assert-equal (#% %** :foo 2) + (dict :foo 2)) + (assert-equal (#%[%3 %2 %1] 1 2 3) + [3 2 1]) + (assert-equal (#%{%1 %2} 10 100) + {10 100}) + (assert-equal (#% #{%3 %2 %1} 1 3 2) + #{3 1 2}) ; sets are not ordered. + (assert-equal (#% "%1") + "%1")) +