From eeef65b505cc0f8b7585db35772428e2b87a24c9 Mon Sep 17 00:00:00 2001 From: Paul Tagliamonte Date: Tue, 31 Dec 2013 13:35:31 -0500 Subject: [PATCH] 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. --- NEWS | 2 -- docs/language/api.rst | 21 +++++++++++---------- eg/python3/futures/hello-world.hy | 2 +- hy/contrib/anaphoric.hy | 10 +++++----- hy/core/macros.hy | 31 ++++++++++++++++++++++++++----- tests/compilers/test_ast.py | 8 ++++---- tests/native_tests/language.hy | 18 +++++++++--------- 7 files changed, 56 insertions(+), 36 deletions(-) diff --git a/NEWS b/NEWS index eea9ae5..cdfb630 100644 --- a/NEWS +++ b/NEWS @@ -55,8 +55,6 @@ Changes from Hy 0.9.11 [ Syntax Fixes ] - * for changed syntax from (for [] ...) -> (for [[]] ...) - * with changed syntax from (with [] ...) -> (with [[]] ...) * get allows multiple arguments diff --git a/docs/language/api.rst b/docs/language/api.rst index 4e70b7d..57c64e7 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -205,11 +205,12 @@ however is called only for every other value in the list. ;; assuming that (side-effect1) and (side-effect2) are functions and ;; collection is a list of numerical values - (for (x collection) (do - (side-effect1 x) - (if (% x 2) - (continue)) - (side-effect2 x))) + (for [x collection] + (do + (side-effect1 x) + (if (% x 2) + (continue)) + (side-effect2 x))) do / progn @@ -489,10 +490,10 @@ collection and calls side-effect to each element in the collection: .. code-block:: clj ;; 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 [[element collection]] (side-effect element) + (for [element collection] (side-effect element) (else (side-effect-2))) 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 - => (for [[element [1 2 3]]] (if (< element 3) + => (for [element [1 2 3]] (if (< element 3) ... (print element) ... (break)) ... (else (print "loop finished"))) 1 2 - => (for [[element [1 2 3]]] (if (< element 4) + => (for [element [1 2 3]] (if (< element 4) ... (print element) ... (break)) ... (else (print "loop finished"))) @@ -680,7 +681,7 @@ function is defined and passed to another function for filtering output. ... {:name "Dave" :age 5}]) => (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))) Alice diff --git a/eg/python3/futures/hello-world.hy b/eg/python3/futures/hello-world.hy index 19d99d4..99f691f 100644 --- a/eg/python3/futures/hello-world.hy +++ b/eg/python3/futures/hello-world.hy @@ -7,5 +7,5 @@ (with-as (ThreadPoolExecutor 10) executor (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))) diff --git a/hy/contrib/anaphoric.hy b/hy/contrib/anaphoric.hy index 9a281d0..2f37b47 100644 --- a/hy/contrib/anaphoric.hy +++ b/hy/contrib/anaphoric.hy @@ -31,14 +31,14 @@ (defmacro ap-each [lst &rest body] "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] "Evalutate the body form for each element in the list while the predicate form evaluates to True." `(let [[p (lambda [it] ~form)]] - (for [[it ~lst]] + (for [it ~lst] (if (p it) ~@body (break))))) @@ -47,7 +47,7 @@ (defmacro ap-map [form lst] "Yield elements evaluated in the form for each element in the list." `(let [[f (lambda [it] ~form)]] - (for [[v ~lst]] + (for [v ~lst] (yield (f v))))) @@ -55,7 +55,7 @@ "Yield elements evaluated for each element in the list when the predicate function returns True." `(let [[f (lambda [it] ~rep)]] - (for [[it ~lst]] + (for [it ~lst] (if (~predfn it) (yield (f it)) (yield it))))) @@ -64,7 +64,7 @@ (defmacro ap-filter [form lst] "Yield elements returned when the predicate form evaluates to True." `(let [[pred (lambda [it] ~form)]] - (for [[val ~lst]] + (for [val ~lst] (if (pred val) (yield val))))) diff --git a/hy/core/macros.hy b/hy/core/macros.hy index a2a240e..cdc5d17 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -25,16 +25,29 @@ ;;; These macros form the hy language ;;; 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] "shorthand for nested for loops: - (for [[x foo] [y bar]] baz) -> + (for [x foo + y bar] + baz) -> (for* [x foo] (for* [y bar] baz))" + + (if (odd? (len args)) + (macro-error args "`for' requires an even number of args.")) + (if (empty? body) (macro-error None "`for' requires a body to evaluate")) (if args - `(for* ~(.pop args 0) (for ~args ~@body)) + `(for* [~(.pop args 0) ~(.pop args 0)] + (for ~args ~@body)) `(do ~@body))) @@ -44,9 +57,17 @@ (with* [x foo] (with* [y bar] baz))" - (if args - `(with* ~(.pop args 0) (with ~args ~@body)) - `(do ~@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))) (defmacro-alias [car first] [thing] diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index b191c17..716f2bb 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -313,7 +313,7 @@ def test_ast_valid_while(): def test_ast_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(): @@ -452,14 +452,14 @@ def test_for_compile_error(): assert(False) try: - can_compile("(fn [] (for [[x]]))") + can_compile("(fn [] (for [x]))") except HyTypeError as e: - assert(e.message == "`for' requires a body to evaluate") + assert(e.message == "`for' requires an even number of args.") else: assert(False) try: - can_compile("(fn [] (for [[x xx]]))") + can_compile("(fn [] (for [x xx]))") except HyTypeError as e: assert(e.message == "`for' requires a body to evaluate") else: diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 8bd1776..30a63e4 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -31,12 +31,12 @@ (defn test-for-loop [] "NATIVE: test for loops?" (setv count 0) - (for [[x [1 2 3 4 5]]] + (for [x [1 2 3 4 5]] (setv count (+ count x))) (assert (= count 15)) (setv count 0) - (for [[x [1 2 3 4 5]] - [y [1 2 3 4 5]]] + (for [x [1 2 3 4 5] + y [1 2 3 4 5]] (setv count (+ count x y))) (assert (= count 150))) @@ -404,9 +404,9 @@ (defn test-yield [] "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) - (for [[y (gen)]] (setv ret (+ ret y))) + (for [y (gen)] (setv ret (+ ret y))) (assert (= ret 10))) @@ -463,7 +463,7 @@ (defn test-for-doodle [] "NATIVE: test for-do" (do (do (do (do (do (do (do (do (do (setv (, x y) (, 0 0))))))))))) - (for [[- [1 2]]] + (for [- [1 2]] (do (setv x (+ x 1)) (setv y (+ y 1)))) @@ -646,7 +646,7 @@ (defn test-nested-if [] "NATIVE: test nested if" - (for [[x (range 10)]] + (for [x (range 10)] (if (in "foo" "foobar") (do (if true true true)) @@ -810,14 +810,14 @@ (defn test-break-breaking [] "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))) (defn test-continue-continuation [] "NATIVE: test checking if continue actually continues" (setv y []) - (for [[x (range 10)]] + (for [x (range 10)] (if (!= x 5) (continue)) (.append y x))