Some macros in boot.clj bind local names, but do not use a vector
in their syntax:

(dotimes i (range 10)
  (prn i))

The attached patch modifies the usage of these macros to be more
like let:

(dotimes [i (range 10)]
  (prn i))

I walked through all the macros in boot.clj and identified and
updated these:
  doseq
  dotimes
  with-open
  when-first
  if-let
  when-let

amap and areduce take names to be bound, but don't take specific
values for those names, so I've left them alone.

All except doseq should produce identical code as before.  doseq
is now based on for, but since it's used in boot.clj before
destructuring is available (as for requires), there's a simpler
version of doseq in play for part of boot.clj.

Under most circumstances if you try to use the old syntax after
this patch, you'll get an error like:

java.lang.IllegalArgumentException: if-let now requires a vector
for its binding

However, some uses are harder to detect, like this old-style use
of destructuring:

(when-let [a b] my-list (dofoo a b))

If you have code like that, you might see a more cryptic error,
such as "Unable to resolve symbol" or perhaps "Unsupported
binding form".

This patch also includes fixes to all the places in boot, proxy,
gen-class, and xml (that I found) that use these macros.  I
assume every lib in clojure-contrib and in fact nearly every
piece of clojure code more that 10 lines long will break.

Have fun!  :-)

--Chouser

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

commit 58a7f4f5735ea227dd3bc1eb54e680d7accd6be2
Author: Chouser <[EMAIL PROTECTED]>
Date:   Fri Nov 7 13:33:10 2008 -0500

    Use vectors for bindings in doseq, et al.

diff --git a/src/clj/clojure/boot.clj b/src/clj/clojure/boot.clj
index 4f51ace..edbac90 100644
--- a/src/clj/clojure/boot.clj
+++ b/src/clj/clojure/boot.clj
@@ -1444,15 +1444,16 @@
   [form] (. clojure.lang.Compiler (eval form)))
 
 (defmacro doseq
-  "Repeatedly executes body (presumably for side-effects) with
-  binding-form bound to successive items from coll.  Does not retain
-  the head of the sequence. Returns nil."
-  [item list & body]
-  `(loop [list# (seq ~list)]
-     (when list#
-       (let [~item (first list#)]
-         [EMAIL PROTECTED])
-       (recur (rest list#)))))
+  "pre-destructuring doseq -- is replaced below"
+  [bindings & body]
+  (if (vector? bindings)
+    `(loop [list# (seq ~(second bindings))]
+       (when list#
+         (let [~(first bindings) (first list#)]
+           [EMAIL PROTECTED])
+         (recur (rest list#))))
+    (throw (IllegalArgumentException.
+             "doseq now requires a vector for bindings"))))
 
 (defn scan [& args] (throw (new Exception "scan is now called dorun")))
 (defn touch [& args] (throw (new Exception "touch is now called doall")))
@@ -1493,7 +1494,7 @@
       (throw (new Exception "Can't await in agent action")))
     (let [latch (new java.util.concurrent.CountDownLatch (count agents))
           count-down (fn [agent] (. latch (countDown)) agent)]
-      (doseq agent agents
+      (doseq [agent agents]
         (send agent count-down))
       (. latch (await))))
 
@@ -1512,19 +1513,26 @@
       (throw (new Exception "Can't await in agent action")))
     (let [latch (new java.util.concurrent.CountDownLatch (count agents))
           count-down (fn [agent] (. latch (countDown)) agent)]
-      (doseq agent agents
+      (doseq [agent agents]
         (send agent count-down))
       (. latch (await  timeout-ms (. java.util.concurrent.TimeUnit MILLISECONDS)))))
   
 (defmacro dotimes
-  "Repeatedly executes body (presumably for side-effects) with name
+  "bindings => name n
+
+  Repeatedly executes body (presumably for side-effects) with name
   bound to integers from 0 through n-1."
-  [i n & body]
-  `(let [n# (int ~n)]
-     (loop [~i (int 0)]
-       (when (< ~i n#)
-         [EMAIL PROTECTED]
-         (recur (unchecked-inc ~i))))))
+  [bindings & body]
+  (if (vector? bindings)
+    (let [i (first bindings)
+          n (second bindings)]
+      `(let [n# (int ~n)]
+         (loop [~i (int 0)]
+           (when (< ~i n#)
+             [EMAIL PROTECTED]
+             (recur (unchecked-inc ~i))))))
+    (throw (IllegalArgumentException.
+             "dotimes now requires a vector for its binding"))))
 
 (defn import
   "import-list => (package-symbol class-name-symbols*)
@@ -1537,7 +1545,7 @@
       (let [#^clojure.lang.Namespace ns *ns*
             pkg (ffirst import-lists)
             classes (rfirst import-lists)]
-        (doseq c classes
+        (doseq [c classes]
           (. ns (importClass c (. Class (forName (str pkg "." c)))))) )
       (apply import (rest import-lists))))
 
@@ -1703,14 +1711,19 @@
   [s] (clojure.lang.RT/readString s))
 
 (defmacro with-open
-  "Evaluates body in a try expression with name bound to the value of
-  init, and a finally clause that calls (. name (close))."
-  [name init & body]
-  `(let [~name ~init]
-     (try
-      [EMAIL PROTECTED]
-      (finally
-       (. ~name (close))))))
+  "bindings => name init
+
+  Evaluates body in a try expression with name bound to the value of
+  init, and a finally clause that calls (.close name)."
+  [bindings & body]
+  (if (vector? bindings)
+    `(let ~bindings
+       (try
+         [EMAIL PROTECTED]
+         (finally
+           (.close ~(first bindings)))))
+    (throw (IllegalArgumentException.
+             "with-open now requires a vector for its binding"))))
 
 (defmacro doto
   "Evaluates x then calls all of the methods with the supplied
@@ -1834,7 +1847,7 @@
   ([#^Class type dim & more-dims]
    (let [dims (cons dim more-dims)
          #^"[I" dimarray (make-array (. Integer TYPE)  (count dims))]
-     (dotimes i (alength dimarray)
+     (dotimes [i (alength dimarray)]
        (aset-int dimarray i (nth dims i)))
      (. Array (newInstance type dimarray)))))
 
@@ -1993,7 +2006,7 @@
   (.unmap (the-ns ns) sym))
 
 ;(defn export [syms]
-;  (doseq sym syms
+;  (doseq [sym syms]
 ;   (.. *ns* (intern sym) (setExported true))))
 
 (defn ns-publics
@@ -2032,7 +2045,7 @@
           rename (or (:rename fs) {})
           exclude (set (:exclude fs))
           to-do (or (:only fs) (keys nspublics))]
-      (doseq sym to-do
+      (doseq [sym to-do]
         (when-not (exclude sym)
           (let [v (nspublics sym)]
             (when-not v
@@ -2268,11 +2281,17 @@
                  [EMAIL PROTECTED])))))))
   
 (defmacro when-first
-  "Same as (when (seq xs) (let [x (first xs)] body))"
-  [x xs & body]
-  `(when (seq ~xs)
-     (let [~x (first ~xs)]
-       [EMAIL PROTECTED])))
+  "bindings => x xs
+
+  Same as (when (seq xs) (let [x (first xs)] body))"
+  [bindings & body]
+  (if (vector? bindings)
+    (let [[x xs] bindings]
+      `(when (seq ~xs)
+         (let [~x (first ~xs)]
+           [EMAIL PROTECTED])))
+    (throw (IllegalArgumentException.
+             "when-first now requires a vector for its binding"))))
 
 (defmacro lazy-cat
   "Expands to code which yields a lazy sequence of the concatenation
@@ -2310,7 +2329,7 @@
         emit (fn emit [[{b :b f :f w :w} & [{ys :e} :as rses]]]
 		  (let [giter (gensym "iter__") gxs (gensym "s__")]
 		    `(fn ~giter [~gxs]
-			 (when-first ~b ~gxs
+			 (when-first [~b ~gxs]
                            (if ~f
 			    ~(if rses
 			       `(let [iterys# ~(emit rses)
@@ -2325,6 +2344,16 @@
     `(let [iter# ~(emit (pargs seq-exprs))]
 	(iter# ~(second seq-exprs))))))
 
+(defmacro doseq
+  "Repeatedly executes body (presumably for side-effects) with
+  bindings and filtering as provided by \"for\".  Does not retain
+  the head of the sequence. Returns nil."
+  [bindings & body]
+  (if (vector? bindings)
+    `(dorun (for ~bindings (do [EMAIL PROTECTED])))
+    (throw (IllegalArgumentException.
+             "doseq now requires a vector for bindings"))))
+
 (defmacro comment
   "Ignores body, yields nil"
   [& body])
@@ -2524,7 +2553,7 @@
   tree, must be a branch."  
   [branch? children root]
     (let [walk (fn walk [nodes]
-                   (when-first node nodes
+                   (when-first [node nodes]
                      (lazy-cons
                       node
                       (if (branch? node)
@@ -2561,7 +2590,7 @@
 (defn slurp
   "Reads the file named by f into a string and returns it."
   [#^String f]
-  (with-open r (new java.io.BufferedReader (new java.io.FileReader f))
+  (with-open [r (new java.io.BufferedReader (new java.io.FileReader f))]
     (let [sb (new StringBuilder)]
       (loop [c (. r (read))]
         (if (neg? c)
@@ -2599,24 +2628,36 @@
                          (lazy-cons f (step r (conj seen f))))))]
       (step (seq coll) #{})))
 
-(defmacro if-let 
-  "if test is true, evaluates then with binding-form bound to the value of test, if not, yields else"
-  ([binding-form test then]
-   `(if-let ~binding-form ~test ~then nil))
-  ([binding-form test then else]
-   `(let [temp# ~test]
-      (if temp# 
-        (let [~binding-form temp#]
-          ~then)
-        ~else))))
-
-(defmacro when-let 
-  "when test is true, evaluates body with binding-form bound to the value of test"
-  [binding-form test & body]
-  `(let [temp# ~test]
-     (when temp#
-       (let [~binding-form temp#]
-         [EMAIL PROTECTED]))))
+(defmacro if-let
+  "bindings => binding-form test
+
+  If test is true, evaluates then with binding-form bound to the value of test, if not, yields else"
+  ([bindings then]
+   `(if-let ~bindings ~then nil))
+  ([bindings then else & oldform]
+   (if (and (vector? bindings) (nil? oldform))
+     (let [[form tst] bindings]
+       `(let [temp# ~tst]
+          (if temp# 
+            (let [~form temp#]
+              ~then)
+            ~else)))
+     (throw (IllegalArgumentException.
+              "if-let now requires a vector for its binding")))))
+
+(defmacro when-let
+  "bindings => binding-form test
+
+  When test is true, evaluates body with binding-form bound to the value of test"
+  [bindings & body]
+  (if (vector? bindings)
+    (let [[form tst] bindings]
+      `(let [temp# ~tst]
+         (when temp#
+           (let [~form temp#]
+             [EMAIL PROTECTED]))))
+    (throw (IllegalArgumentException.
+             "when-let now requires a vector for its binding"))))
 
 (defn replace
   "Given a map of replacement pairs and a vector/collection, returns a
@@ -2625,11 +2666,11 @@
   [smap coll]
     (if (vector? coll)
       (reduce (fn [v i]
-                (if-let e (find smap (nth v i))
+                (if-let [e (find smap (nth v i))]
                         (assoc v i (val e))
                         v))
               coll (range (count coll)))
-      (map #(if-let e (find smap %) (val e) %) coll)))
+      (map #(if-let [e (find smap %)] (val e) %) coll)))
 
 (defmacro dosync 
   "Runs the exprs (in an implicit do) in a transaction that encompasses
@@ -2669,11 +2710,11 @@
   ([#^clojure.lang.Sorted sc test key]
    (let [include (bound-fn sc test key)]
      (if (#{> >=} test)
-       (when-let [e :as s] (. sc seqFrom key true)
+       (when-let [[e :as s] (. sc seqFrom key true)]
          (if (include e) s (rest s)))
        (take-while include (. sc seq true)))))     
   ([#^clojure.lang.Sorted sc start-test start-key end-test end-key]
-   (when-let [e :as s] (. sc seqFrom start-key true)
+   (when-let [[e :as s] (. sc seqFrom start-key true)]
      (take-while (bound-fn sc end-test end-key)
                  (if ((bound-fn sc start-test start-key) e) s (rest s))))))
 
@@ -2684,11 +2725,11 @@
   ([#^clojure.lang.Sorted sc test key]
    (let [include (bound-fn sc test key)]
      (if (#{< <=} test)
-       (when-let [e :as s] (. sc seqFrom key false)
+       (when-let [[e :as s] (. sc seqFrom key false)]
          (if (include e) s (rest s)))
        (take-while include (. sc seq false)))))     
   ([#^clojure.lang.Sorted sc start-test start-key end-test end-key]
-   (when-let [e :as s] (. sc seqFrom end-key false)
+   (when-let [[e :as s] (. sc seqFrom end-key false)]
      (take-while (bound-fn sc start-test start-key)
                  (if ((bound-fn sc end-test end-key) e) s (rest s))))))
 
@@ -3186,7 +3227,7 @@
       (when use
         (when *loading-verbosely*
           (printf "(clojure/refer '%s" lib)
-          (doseq opt filter-opts
+          (doseq [opt filter-opts]
             (printf " %s '%s" (key opt) (print-str (val opt))))
           (printf ")\n"))
         (apply refer lib (mapcat seq filter-opts))))))
@@ -3198,12 +3239,12 @@
   (let [flags (filter keyword? args)
         opts (interleave flags (repeat true))
         args (filter (complement keyword?) args)]
-    (doseq arg args
+    (doseq [arg args]
       (if (libspec? arg)
         (apply load-lib nil (prependss arg opts))
         (let [[prefix & args] arg]
           (throw-if (nil? prefix) "prefix cannot be nil")
-          (doseq arg args
+          (doseq [arg args]
             (apply load-lib prefix (prependss arg opts))))))))
 
 ;; Public
@@ -3280,7 +3321,7 @@
   classpath-relative if it begins with a slash or relative to the root
   directory for the current namespace otherwise."
   [& paths]
-  (doseq path paths
+  (doseq [path paths]
     (let [path (if (.startsWith path "/")
                  path
                  (str (root-directory (ns-name *ns*)) \/ path))]
@@ -3458,7 +3499,7 @@
       (.write w "#")
       (do
         (.write w begin)
-        (when-let xs (seq sequence)
+        (when-let [xs (seq sequence)]
           (if *print-length*
             (loop [[x & xs] xs
                    print-length *print-length*]
@@ -3477,7 +3518,7 @@
         (.write w end)))))
 
 (defn- print-meta [o, #^Writer w]
-  (when-let m (meta o)
+  (when-let [m (meta o)]
     (when (and *print-meta* *print-readably* (pos? (count m)))
       (.write w "#^")
       (if (and (= (count m) 1) (:tag m))
@@ -3549,7 +3590,7 @@
 (defmethod print-method String [#^String s, #^Writer w]
   (if *print-readably*
     (do (.append w \")
-      (dotimes n (count s)
+      (dotimes [n (count s)]
         (let [c (.charAt s n)
               e (char-escape-string c)]
           (if e (.write w e) (.append w c))))
diff --git a/src/clj/clojure/genclass.clj b/src/clj/clojure/genclass.clj
index d2d10a1..49e9ad4 100644
--- a/src/clj/clojure/genclass.clj
+++ b/src/clj/clojure/genclass.clj
@@ -232,7 +232,7 @@
                 (when-not as-static
                   (. gen (loadThis)))
                                         ;box args
-                (dotimes i (count ptypes)
+                (dotimes [i (count ptypes)]
                   (. gen (loadArg i))
                   (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i))))
                                         ;call fn
@@ -265,7 +265,7 @@
                    (into-array (map iname interfaces)))))
     
                                         ;static fields for vars
-    (doseq v var-fields
+    (doseq [v var-fields]
       (. cv (visitField (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_FINAL) (. Opcodes ACC_STATIC))
                         (var-name v) 
                         (. var-type getDescriptor)
@@ -283,7 +283,7 @@
                    (. Method getMethod "void <clinit> ()")
                    nil nil cv)]
       (. gen (visitCode))
-      (doseq v var-fields
+      (doseq [v var-fields]
         (. gen push pkg-name)
         (. gen push (str sname "-" v))
         (. gen (invokeStatic rt-type (. Method (getMethod "clojure.lang.Var var(String,String)"))))
@@ -298,7 +298,7 @@
       (. gen (endMethod)))
     
                                         ;ctors
-    (doseq [pclasses super-pclasses] ctor-sig-map
+    (doseq [[pclasses super-pclasses] ctor-sig-map]
       (let [ptypes (to-types pclasses)
             super-ptypes (to-types super-pclasses)
             m (new Method "<init>" (. Type VOID_TYPE) ptypes)
@@ -316,7 +316,7 @@
             (. gen dup)
             (. gen ifNull no-init-label)
                                         ;box init args
-            (dotimes i (count pclasses)
+            (dotimes [i (count pclasses)]
               (. gen (loadArg i))
               (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i))))
                                         ;call init fn
@@ -330,7 +330,7 @@
             
             (. gen (loadThis))
             (. gen dupX1)
-            (dotimes i (count super-pclasses)
+            (dotimes [i (count super-pclasses)]
               (. gen loadLocal local)
               (. gen push i)
               (. gen (invokeStatic rt-type nth-method))
@@ -373,7 +373,7 @@
     
                                         ;add methods matching supers', if no fn -> call super
     (let [mm (non-private-methods super)]
-      (doseq #^java.lang.reflect.Method meth (vals mm)
+      (doseq [#^java.lang.reflect.Method meth (vals mm)]
              (emit-forwarding-method (.getName meth) (.getParameterTypes meth) (.getReturnType meth) false
                                      (fn [gen m]
                                        (. gen (loadThis))
@@ -394,7 +394,7 @@
                     (assoc mm (method-sig meth) meth))))
               mm (mapcat #(.getMethods %) interfaces))
                                         ;extra methods
-       (doseq [mname pclasses rclass :as msig] methods
+       (doseq [[mname pclasses rclass :as msig] methods]
          (emit-forwarding-method (str mname) pclasses rclass (:static ^msig)
                                  emit-unsupported)))
 
@@ -423,7 +423,7 @@
         (. gen (returnValue))
         (. gen (endMethod))))
                                         ;field exposers
-    (doseq [f {getter :get setter :set}] exposes
+    (doseq [[f {getter :get setter :set}] exposes]
       (let [fld (.getDeclaredField super (str f))
             ftype (totype (.getType fld))]
         (when getter
@@ -469,7 +469,7 @@
   (let [{:keys [name bytecode]} (apply gen-class (str name) options)
         file (java.io.File. path (str (. name replace \. (. java.io.File separatorChar)) ".class"))]
     (.createNewFile file)
-    (with-open f (java.io.FileOutputStream. file)
+    (with-open [f (java.io.FileOutputStream. file)]
       (.write f bytecode))))
 
 (comment
diff --git a/src/clj/clojure/proxy.clj b/src/clj/clojure/proxy.clj
index f72111a..2bf37b8 100644
--- a/src/clj/clojure/proxy.clj
+++ b/src/clj/clojure/proxy.clj
@@ -70,7 +70,7 @@
                                         ;if found
                     (. gen (loadThis))
                                         ;box args
-                    (dotimes i (count ptypes)
+                    (dotimes [i (count ptypes)]
                       (. gen (loadArg i))
                       (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i))))
                                         ;call fn
@@ -101,7 +101,7 @@
             (. cv (visitField (+ (. Opcodes ACC_PRIVATE) (. Opcodes ACC_VOLATILE))
                               fmap (. map-type (getDescriptor)) nil nil))          
                                         ;add ctors matching/calling super's
-            (doseq #^Constructor ctor (. super (getDeclaredConstructors))
+            (doseq [#^Constructor ctor (. super (getDeclaredConstructors))]
               (when-not (. Modifier (isPrivate (. ctor (getModifiers))))
                 (let [ptypes (to-types (. ctor (getParameterTypes)))
                       m (new Method "<init>" (. Type VOID_TYPE) ptypes)
@@ -165,7 +165,7 @@
                            (recur mm considered (. c (getSuperclass))))
                          mm))]
                                         ;add methods matching supers', if no mapping -> call super
-              (doseq #^java.lang.reflect.Method meth (vals mm)
+              (doseq [#^java.lang.reflect.Method meth (vals mm)]
                      (gen-method meth 
                                  (fn [gen m]
                                    (. gen (loadThis))
@@ -178,8 +178,8 @@
                                                            (. m (getDescriptor)))))))
               
                                         ;add methods matching interfaces', if no mapping -> throw
-              (doseq #^Class iface interfaces
-                (doseq #^java.lang.reflect.Method meth (. iface (getMethods))
+              (doseq [#^Class iface interfaces]
+                (doseq [#^java.lang.reflect.Method meth (. iface (getMethods))]
                    (when-not (contains? mm (method-sig meth))
                      (gen-method meth 
                                  (fn [gen m]
diff --git a/src/clj/clojure/xml/xml.clj b/src/clj/clojure/xml/xml.clj
index 516eb06..117016a 100644
--- a/src/clj/clojure/xml/xml.clj
+++ b/src/clj/clojure/xml/xml.clj
@@ -95,12 +95,12 @@
     (do
       (print (str "<" (name (:tag e))))
       (when (:attrs e)
-	(doseq attr (:attrs e)
+	(doseq [attr (:attrs e)]
 	  (print (str " " (name (key attr)) "='" (val attr)"'"))))
       (if (:content e)
 	(do
 	  (println ">")
-	  (doseq c (:content e)
+	  (doseq [c (:content e)]
 	    (emit-element c))
 	  (println (str "</" (name (:tag e)) ">")))
 	(println "/>")))))

Reply via email to