From 0bf1084d8cdfeddeca119dfe2dcb2e2d5e9d6572 Mon Sep 17 00:00:00 2001 From: gilch Date: Tue, 11 Aug 2015 18:11:33 -0600 Subject: [PATCH] added xi-forms These work like Clojure's `#()` anonymous function literals. --- docs/contrib/anaphoric.rst | 28 ++++++++++++++++++++----- hy/contrib/anaphoric.hy | 24 +++++++++++++++++++++ tests/native_tests/contrib/anaphoric.hy | 22 +++++++++++++++++++ 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/docs/contrib/anaphoric.rst b/docs/contrib/anaphoric.rst index 750a470..a241cd3 100644 --- a/docs/contrib/anaphoric.rst +++ b/docs/contrib/anaphoric.rst @@ -13,14 +13,11 @@ concise and easy to read. -- Wikipedia (http://en.wikipedia.org/wiki/Anaphoric_macro) -Macros -====== - .. _ap-if: ap-if -------- +===== Usage: ``(ap-if (foo) (print it))`` @@ -31,7 +28,7 @@ true and false branches. .. _ap-each: ap-each -------- +======= Usage: ``(ap-each [1 2 3 4 5] (print it))`` @@ -228,3 +225,24 @@ Returns a function which applies several forms in series from left to right. The => (def op (ap-compose (+ it 1) (* it 3))) => (op 2) 9 + +.. _xi + +xi +== + +Usage ``(xi function body ...)`` + +Returns a function with parameters implicitly determined by the presence in the body of xi parameters. An xi symbol designates the ith parameter (1-based, e.g. x1, x2, x3, etc.), or all remaining parameters for xi itself. This is not a replacement for lambda. The xi forms cannot be nested. + +This is similar to Clojure's anonymous function literals (``#()``). + +.. code-block:: hy + + => ((xi identity [x1 x5 [x2 x3] xi x4]) 1 2 3 4 5 6 7 8) + [1, 5, [2, 3,] (6, 7, 8), 4] + => (def add-10 (xi + 10 x1)) + => (add-10 6) + 16 + + diff --git a/hy/contrib/anaphoric.hy b/hy/contrib/anaphoric.hy index 628ae9d..0584a76 100644 --- a/hy/contrib/anaphoric.hy +++ b/hy/contrib/anaphoric.hy @@ -121,3 +121,27 @@ (defmacro ap-compose [&rest forms] "Returns a function which is the composition of several forms." `(fn [var] (ap-pipe var ~@forms))) + +(defmacro xi [function &rest body] + "Returns a function with parameters implicitly determined by the presence in + the body of xi parameters. An xi symbol designates the ith parameter + (1-based, e.g. x1, x2, x3, etc.), or all remaining parameters for xi itself. + This is not a replacement for lambda. The xi forms cannot be nested. " + (setv flatbody (flatten body)) + `(lambda [;; generate all xi symbols up to the maximum found in body + ~@(genexpr (HySymbol (+ "x" + (str i))) + [i (range 1 + ;; find the maximum xi + (inc (max (genexpr (int (cdr a)) + [a flatbody] + (and (symbol? a) + (.startswith a 'x) + (.isdigit (cdr a)))) + :default 0)))]) + ;; generate the &rest paremeter only if 'xi is present in body + ~@(if (in 'xi flatbody) + '(&rest xi) + '())] + (~function ~@body))) + diff --git a/tests/native_tests/contrib/anaphoric.hy b/tests/native_tests/contrib/anaphoric.hy index 8b37905..20b7848 100644 --- a/tests/native_tests/contrib/anaphoric.hy +++ b/tests/native_tests/contrib/anaphoric.hy @@ -113,3 +113,25 @@ "NATIVE: testing anaphoric compose" (assert-equal ((ap-compose (+ it 1) (* it 3)) 2) 9) (assert-equal ((ap-compose (list (rest it)) (len it)) [4 5 6 7]) 3)) + +(defn test-xi [] + "NATIVE: testing xi forms" + ;; test ordering + (assert-equal ((xi / x1 x2) 2 4) 0.5) + (assert-equal ((xi / x2 x1) 2 4) 2) + (assert-equal ((xi identity (, x5 x4 x3 x2 x1)) 1 2 3 4 5) (, 5 4 3 2 1)) + (assert-equal ((xi identity (, x1 x2 x3 x4 x5)) 1 2 3 4 5) (, 1 2 3 4 5)) + (assert-equal ((xi identity (, x1 x5 x2 x3 x4)) 1 2 3 4 5) (, 1 5 2 3 4)) + ;; test &rest + (assert-equal ((xi sum xi) 1 2 3) 6) + (assert-equal ((xi identity (, x1 xi)) 10 1 2 3) (, 10 (, 1 2 3))) + ;; no parameters + (assert-equal ((xi list)) []) + (assert-equal ((xi identity "Hy!")) "Hy!") + (assert-equal ((xi identity "xi")) "xi") + (assert-equal ((xi + "Hy " "world!")) "Hy world!") + ;; test skipped parameters + (assert-equal ((xi identity [x3 x1]) 1 2 3) [3 1]) + ;; test nesting + (assert-equal ((xi identity [x1 (, x2 [x3] "Hy" [xi])]) 1 2 3 4 5) + [1 (, 2 [3] "Hy" [(, 4 5)])]))