Change the signature of (for) and (with).

(for) is restored to clojure-like flatness.
 (with) can be used without the nested list if you don't care about the
        name.

   Tests and examples updated.
This commit is contained in:
Paul Tagliamonte 2013-12-31 13:35:31 -05:00
parent 8cabf22749
commit eeef65b505
7 changed files with 56 additions and 36 deletions

2
NEWS
View File

@ -55,8 +55,6 @@ Changes from Hy 0.9.11
[ Syntax Fixes ] [ Syntax Fixes ]
* for changed syntax from (for [] ...) -> (for [[]] ...)
* with changed syntax from (with [] ...) -> (with [[]] ...)
* get allows multiple arguments * get allows multiple arguments

View File

@ -205,7 +205,8 @@ however is called only for every other value in the list.
;; assuming that (side-effect1) and (side-effect2) are functions and ;; assuming that (side-effect1) and (side-effect2) are functions and
;; collection is a list of numerical values ;; collection is a list of numerical values
(for (x collection) (do (for [x collection]
(do
(side-effect1 x) (side-effect1 x)
(if (% x 2) (if (% x 2)
(continue)) (continue))
@ -489,10 +490,10 @@ collection and calls side-effect to each element in the collection:
.. code-block:: clj .. code-block:: clj
;; assuming that (side-effect) is a function that takes a single parameter ;; assuming that (side-effect) is a function that takes a single parameter
(for [[element collection]] (side-effect element)) (for [element collection] (side-effect element))
;; for can have an optional else block ;; for can have an optional else block
(for [[element collection]] (side-effect element) (for [element collection] (side-effect element)
(else (side-effect-2))) (else (side-effect-2)))
The optional `else` block is executed only if the `for` loop terminates The optional `else` block is executed only if the `for` loop terminates
@ -500,14 +501,14 @@ normally. If the execution is halted with `break`, the `else` does not execute.
.. code-block:: clj .. code-block:: clj
=> (for [[element [1 2 3]]] (if (< element 3) => (for [element [1 2 3]] (if (< element 3)
... (print element) ... (print element)
... (break)) ... (break))
... (else (print "loop finished"))) ... (else (print "loop finished")))
1 1
2 2
=> (for [[element [1 2 3]]] (if (< element 4) => (for [element [1 2 3]] (if (< element 4)
... (print element) ... (print element)
... (break)) ... (break))
... (else (print "loop finished"))) ... (else (print "loop finished")))
@ -680,7 +681,7 @@ function is defined and passed to another function for filtering output.
... {:name "Dave" :age 5}]) ... {:name "Dave" :age 5}])
=> (defn display-people [people filter] => (defn display-people [people filter]
... (for [[person people]] (if (filter person) (print (:name person))))) ... (for [person people] (if (filter person) (print (:name person)))))
=> (display-people people (fn [person] (< (:age person) 25))) => (display-people people (fn [person] (< (:age person) 25)))
Alice Alice

View File

@ -7,5 +7,5 @@
(with-as (ThreadPoolExecutor 10) executor (with-as (ThreadPoolExecutor 10) executor
(setv jobs (list-comp (.submit executor task-to-do) (x (range 0 10)))) (setv jobs (list-comp (.submit executor task-to-do) (x (range 0 10))))
(for (future (as-completed jobs)) (for [future (as-completed jobs)]
(.result future))) (.result future)))

View File

@ -31,14 +31,14 @@
(defmacro ap-each [lst &rest body] (defmacro ap-each [lst &rest body]
"Evaluate the body form for each element in the list." "Evaluate the body form for each element in the list."
`(for [[it ~lst]] ~@body)) `(for [it ~lst] ~@body))
(defmacro ap-each-while [lst form &rest body] (defmacro ap-each-while [lst form &rest body]
"Evalutate the body form for each element in the list while the "Evalutate the body form for each element in the list while the
predicate form evaluates to True." predicate form evaluates to True."
`(let [[p (lambda [it] ~form)]] `(let [[p (lambda [it] ~form)]]
(for [[it ~lst]] (for [it ~lst]
(if (p it) (if (p it)
~@body ~@body
(break))))) (break)))))
@ -47,7 +47,7 @@
(defmacro ap-map [form lst] (defmacro ap-map [form lst]
"Yield elements evaluated in the form for each element in the list." "Yield elements evaluated in the form for each element in the list."
`(let [[f (lambda [it] ~form)]] `(let [[f (lambda [it] ~form)]]
(for [[v ~lst]] (for [v ~lst]
(yield (f v))))) (yield (f v)))))
@ -55,7 +55,7 @@
"Yield elements evaluated for each element in the list when the "Yield elements evaluated for each element in the list when the
predicate function returns True." predicate function returns True."
`(let [[f (lambda [it] ~rep)]] `(let [[f (lambda [it] ~rep)]]
(for [[it ~lst]] (for [it ~lst]
(if (~predfn it) (if (~predfn it)
(yield (f it)) (yield (f it))
(yield it))))) (yield it)))))
@ -64,7 +64,7 @@
(defmacro ap-filter [form lst] (defmacro ap-filter [form lst]
"Yield elements returned when the predicate form evaluates to True." "Yield elements returned when the predicate form evaluates to True."
`(let [[pred (lambda [it] ~form)]] `(let [[pred (lambda [it] ~form)]]
(for [[val ~lst]] (for [val ~lst]
(if (pred val) (if (pred val)
(yield val))))) (yield val)))))

View File

@ -25,16 +25,29 @@
;;; These macros form the hy language ;;; These macros form the hy language
;;; They are automatically required in every module, except inside hy.core ;;; They are automatically required in every module, except inside hy.core
(import [hy.models.list [HyList]]
[hy.models.symbol [HySymbol]])
(defmacro for [args &rest body] (defmacro for [args &rest body]
"shorthand for nested for loops: "shorthand for nested for loops:
(for [[x foo] [y bar]] baz) -> (for [x foo
y bar]
baz) ->
(for* [x foo] (for* [x foo]
(for* [y bar] (for* [y bar]
baz))" baz))"
(if (odd? (len args))
(macro-error args "`for' requires an even number of args."))
(if (empty? body) (if (empty? body)
(macro-error None "`for' requires a body to evaluate")) (macro-error None "`for' requires a body to evaluate"))
(if args (if args
`(for* ~(.pop args 0) (for ~args ~@body)) `(for* [~(.pop args 0) ~(.pop args 0)]
(for ~args ~@body))
`(do ~@body))) `(do ~@body)))
@ -44,8 +57,16 @@
(with* [x foo] (with* [x foo]
(with* [y bar] (with* [y bar]
baz))" baz))"
(if args
`(with* ~(.pop args 0) (with ~args ~@body)) (if (not (empty? args))
(let [[primary (.pop args 0)]]
(if (isinstance primary HyList)
;;; OK. if we have a list, we can go ahead and unpack that
;;; as the argument to with.
`(with* [~@primary] (with ~args ~@body))
;;; OK, let's just give it away. This may not be something we
;;; can do, but that's really the programmer's problem.
`(with* [~primary] (with ~args ~@body))))
`(do ~@body))) `(do ~@body)))

View File

@ -313,7 +313,7 @@ def test_ast_valid_while():
def test_ast_valid_for(): def test_ast_valid_for():
"Make sure AST can compile valid for" "Make sure AST can compile valid for"
can_compile("(for [[a 2]] (print a))") can_compile("(for [a 2] (print a))")
def test_ast_invalid_for(): def test_ast_invalid_for():
@ -452,14 +452,14 @@ def test_for_compile_error():
assert(False) assert(False)
try: try:
can_compile("(fn [] (for [[x]]))") can_compile("(fn [] (for [x]))")
except HyTypeError as e: except HyTypeError as e:
assert(e.message == "`for' requires a body to evaluate") assert(e.message == "`for' requires an even number of args.")
else: else:
assert(False) assert(False)
try: try:
can_compile("(fn [] (for [[x xx]]))") can_compile("(fn [] (for [x xx]))")
except HyTypeError as e: except HyTypeError as e:
assert(e.message == "`for' requires a body to evaluate") assert(e.message == "`for' requires a body to evaluate")
else: else:

View File

@ -31,12 +31,12 @@
(defn test-for-loop [] (defn test-for-loop []
"NATIVE: test for loops?" "NATIVE: test for loops?"
(setv count 0) (setv count 0)
(for [[x [1 2 3 4 5]]] (for [x [1 2 3 4 5]]
(setv count (+ count x))) (setv count (+ count x)))
(assert (= count 15)) (assert (= count 15))
(setv count 0) (setv count 0)
(for [[x [1 2 3 4 5]] (for [x [1 2 3 4 5]
[y [1 2 3 4 5]]] y [1 2 3 4 5]]
(setv count (+ count x y))) (setv count (+ count x y)))
(assert (= count 150))) (assert (= count 150)))
@ -404,9 +404,9 @@
(defn test-yield [] (defn test-yield []
"NATIVE: test yielding" "NATIVE: test yielding"
(defn gen [] (for [[x [1 2 3 4]]] (yield x))) (defn gen [] (for [x [1 2 3 4]] (yield x)))
(setv ret 0) (setv ret 0)
(for [[y (gen)]] (setv ret (+ ret y))) (for [y (gen)] (setv ret (+ ret y)))
(assert (= ret 10))) (assert (= ret 10)))
@ -463,7 +463,7 @@
(defn test-for-doodle [] (defn test-for-doodle []
"NATIVE: test for-do" "NATIVE: test for-do"
(do (do (do (do (do (do (do (do (do (setv (, x y) (, 0 0))))))))))) (do (do (do (do (do (do (do (do (do (setv (, x y) (, 0 0)))))))))))
(for [[- [1 2]]] (for [- [1 2]]
(do (do
(setv x (+ x 1)) (setv x (+ x 1))
(setv y (+ y 1)))) (setv y (+ y 1))))
@ -646,7 +646,7 @@
(defn test-nested-if [] (defn test-nested-if []
"NATIVE: test nested if" "NATIVE: test nested if"
(for [[x (range 10)]] (for [x (range 10)]
(if (in "foo" "foobar") (if (in "foo" "foobar")
(do (do
(if true true true)) (if true true true))
@ -810,14 +810,14 @@
(defn test-break-breaking [] (defn test-break-breaking []
"NATIVE: test checking if break actually breaks" "NATIVE: test checking if break actually breaks"
(defn holy-grail [] (for [[x (range 10)]] (if (= x 5) (break))) x) (defn holy-grail [] (for [x (range 10)] (if (= x 5) (break))) x)
(assert (= (holy-grail) 5))) (assert (= (holy-grail) 5)))
(defn test-continue-continuation [] (defn test-continue-continuation []
"NATIVE: test checking if continue actually continues" "NATIVE: test checking if continue actually continues"
(setv y []) (setv y [])
(for [[x (range 10)]] (for [x (range 10)]
(if (!= x 5) (if (!= x 5)
(continue)) (continue))
(.append y x)) (.append y x))