Merge pull request #1444 from gilch/xi-tag
change xi macro to #% tag macro
This commit is contained in:
commit
fac87c99d0
2
NEWS
2
NEWS
@ -22,6 +22,8 @@ Changes from 0.13.0
|
||||
* support EDN `#_` syntax to discard the next term
|
||||
* `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
|
||||
* `#%` 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
|
||||
|
@ -229,21 +229,37 @@ Returns a function which applies several forms in series from left to right. The
|
||||
=> (op 2)
|
||||
9
|
||||
|
||||
.. _xi
|
||||
.. _#%
|
||||
|
||||
xi
|
||||
#%
|
||||
==
|
||||
|
||||
Usage ``(xi body ...)``
|
||||
Usage ``#% expr``
|
||||
|
||||
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 fn. The xi forms cannot be nested.
|
||||
Makes an expression into a function with an implicit ``%`` parameter list.
|
||||
|
||||
This is similar to Clojure's anonymous function literals (``#()``).
|
||||
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.
|
||||
|
||||
.. 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
|
||||
=> (#%[%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.
|
||||
|
||||
|
@ -5,11 +5,10 @@
|
||||
|
||||
;;; These macros make writing functional programs more concise
|
||||
|
||||
|
||||
(defmacro ap-if [test-form then-form &optional else-form]
|
||||
`(do
|
||||
(setv it ~test-form)
|
||||
(if it ~then-form ~else-form)))
|
||||
(setv it ~test-form)
|
||||
(if it ~then-form ~else-form)))
|
||||
|
||||
|
||||
(defmacro ap-each [lst &rest body]
|
||||
@ -25,17 +24,17 @@
|
||||
(defn ~p [it] ~form)
|
||||
(for [it ~lst]
|
||||
(if (~p it)
|
||||
~@body
|
||||
(break)))))
|
||||
~@body
|
||||
(break)))))
|
||||
|
||||
|
||||
(defmacro ap-map [form lst]
|
||||
"Yield elements evaluated in the form for each element in the list."
|
||||
(setv v (gensym 'v) f (gensym 'f))
|
||||
`((fn []
|
||||
(defn ~f [it] ~form)
|
||||
(for [~v ~lst]
|
||||
(yield (~f ~v))))))
|
||||
(defn ~f [it] ~form)
|
||||
(for [~v ~lst]
|
||||
(yield (~f ~v))))))
|
||||
|
||||
|
||||
(defmacro ap-map-when [predfn rep lst]
|
||||
@ -43,21 +42,21 @@
|
||||
predicate function returns True."
|
||||
(setv f (gensym))
|
||||
`((fn []
|
||||
(defn ~f [it] ~rep)
|
||||
(for [it ~lst]
|
||||
(if (~predfn it)
|
||||
(yield (~f it))
|
||||
(yield it))))))
|
||||
(defn ~f [it] ~rep)
|
||||
(for [it ~lst]
|
||||
(if (~predfn it)
|
||||
(yield (~f it))
|
||||
(yield it))))))
|
||||
|
||||
|
||||
(defmacro ap-filter [form lst]
|
||||
"Yield elements returned when the predicate form evaluates to True."
|
||||
(setv pred (gensym))
|
||||
`((fn []
|
||||
(defn ~pred [it] ~form)
|
||||
(for [val ~lst]
|
||||
(if (~pred val)
|
||||
(yield val))))))
|
||||
(defn ~pred [it] ~form)
|
||||
(for [val ~lst]
|
||||
(if (~pred val)
|
||||
(yield val))))))
|
||||
|
||||
|
||||
(defmacro ap-reject [form lst]
|
||||
@ -95,10 +94,10 @@
|
||||
(defmacro ap-reduce [form lst &optional [initial-value None]]
|
||||
"Anaphoric form of reduce, `acc' and `it' can be used for a form"
|
||||
`(do
|
||||
(setv acc ~(if (none? initial-value) `(get ~lst 0) initial-value))
|
||||
(ap-each ~(if (none? initial-value) `(cut ~lst 1) lst)
|
||||
(setv acc ~form))
|
||||
acc))
|
||||
(setv acc ~(if (none? initial-value) `(get ~lst 0) initial-value))
|
||||
(ap-each ~(if (none? initial-value) `(cut ~lst 1) lst)
|
||||
(setv acc ~form))
|
||||
acc))
|
||||
|
||||
|
||||
(defmacro ap-pipe [var &rest forms]
|
||||
@ -112,26 +111,32 @@
|
||||
"Returns a function which is the composition of several forms."
|
||||
`(fn [var] (ap-pipe var ~@forms)))
|
||||
|
||||
(defmacro xi [&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 fn. The xi forms cannot be nested. "
|
||||
(setv flatbody (flatten body))
|
||||
`(fn [;; 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 (+ (list-comp (int (cut a 1))
|
||||
[a flatbody]
|
||||
(and (symbol? a)
|
||||
(.startswith a 'x)
|
||||
(.isdigit (cut a 1))))
|
||||
[0]))))])
|
||||
;; generate the &rest parameter only if 'xi is present in body
|
||||
~@(if (in 'xi flatbody)
|
||||
'(&rest xi)
|
||||
'())]
|
||||
(~@body)))
|
||||
(deftag % [expr]
|
||||
"Makes an expression into a function with an implicit `%` parameter list.
|
||||
|
||||
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 %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 %*))
|
||||
;; similarly for &kwargs and %**
|
||||
~@(if (in '%** %symbols)
|
||||
'(&kwargs %**))]
|
||||
~expr))
|
||||
|
||||
|
@ -65,9 +65,9 @@
|
||||
(defn test-ap-dotimes []
|
||||
"NATIVE: testing anaphoric dotimes"
|
||||
(assert-equal (do (setv n []) (ap-dotimes 3 (.append n 3)) n)
|
||||
[3 3 3])
|
||||
[3 3 3])
|
||||
(assert-equal (do (setv n []) (ap-dotimes 3 (.append n it)) n)
|
||||
[0 1 2]))
|
||||
[0 1 2]))
|
||||
|
||||
(defn test-ap-first []
|
||||
"NATIVE: testing anaphoric first"
|
||||
@ -86,41 +86,59 @@
|
||||
(assert-equal (ap-reduce (* acc it) [1 2 3]) 6)
|
||||
(assert-equal (ap-reduce (* acc it) [1 2 3] 6) 36)
|
||||
(assert-equal (ap-reduce (+ acc " on " it) ["Hy" "meth"])
|
||||
"Hy on meth")
|
||||
"Hy on meth")
|
||||
(assert-equal (ap-reduce (+ acc it) [] 1) 1))
|
||||
|
||||
|
||||
(defn test-ap-pipe []
|
||||
"NATIVE: testing anaphoric pipe"
|
||||
(assert-equal (ap-pipe 2 (+ it 1) (* it 3)) 9)
|
||||
(assert-equal (ap-pipe [4 5 6 7] (list (rest it)) (len it)) 3))
|
||||
|
||||
|
||||
(defn test-ap-compose []
|
||||
"NATIVE: testing anaphoric compose"
|
||||
"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"
|
||||
(defn test-tag-fn []
|
||||
"NATIVE: testing #%() 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))
|
||||
(assert-equal (#%(/ %1 %2) 2 4) 0.5)
|
||||
(assert-equal (#%(/ %2 %1) 2 4) 2)
|
||||
(assert-equal (#%(identity (, %5 %4 %3 %2 %1)) 1 2 3 4 5) (, 5 4 3 2 1))
|
||||
(assert-equal (#%(identity (, %1 %2 %3 %4 %5)) 1 2 3 4 5) (, 1 2 3 4 5))
|
||||
(assert-equal (#%(identity (, %1 %5 %2 %3 %4)) 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)))
|
||||
(assert-equal (#%(sum %*) 1 2 3) 6)
|
||||
(assert-equal (#%(identity (, %1 %*)) 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!")
|
||||
(assert-equal (#%(list)) [])
|
||||
(assert-equal (#%(identity "Hy!")) "Hy!")
|
||||
(assert-equal (#%(identity "%*")) "%*")
|
||||
(assert-equal (#%(+ "Hy " "world!")) "Hy world!")
|
||||
;; test skipped parameters
|
||||
(assert-equal ((xi identity [x3 x1]) 1 2 3) [3 1])
|
||||
(assert-equal (#%(identity [%3 %1]) 1 2 3) [3 1])
|
||||
;; test nesting
|
||||
(assert-equal ((xi identity [x1 (, x2 [x3] "Hy" [xi])]) 1 2 3 4 5)
|
||||
(assert-equal (#%(identity [%1 (, %2 [%3] "Hy" [%*])]) 1 2 3 4 5)
|
||||
[1 (, 2 [3] "Hy" [(, 4 5)])])
|
||||
;; test arg as function
|
||||
(assert-equal ((xi x1 2 4) +) 6)
|
||||
(assert-equal ((xi x1 2 4) -) -2)
|
||||
(assert-equal ((xi x1 2 4) /) 0.5))
|
||||
(assert-equal (#%(%1 2 4) +) 6)
|
||||
(assert-equal (#%(%1 2 4) -) -2)
|
||||
(assert-equal (#%(%1 2 4) /) 0.5)
|
||||
;; test &rest &kwargs
|
||||
(assert-equal (#%(, %* %**) 1 2 :a 'b)
|
||||
(, (, 1 2)
|
||||
(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"))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user