I want to send var-args functions to a macro, still like var-args. Here is my code:
(defmacro test-macro [& args] `(println (str "count=" ~(count args) "; args=" ~@args ))) (defn test-fn-calling-macro [& args] (test-macro args))
The result (test-macro "a" "b" "c") is what I want: count=3; args=abc count=3; args=abc
Output (test-fn-calling-macro "a" "b" "c") : count=1; args=("a" "b" "c") count=1; args=("a" "b" "c") because args is sent as a single argument to the macro. How can I extend this argument in my function to call a macro with three arguments?
I guess I just missed the simple main function, but I can't find it. Thanks
EDIT 2 . My "real" code, shown in the EDIT section below, is not a valid situation for using this technique.
As @Brian pointed out, the xml-to-cass can be replaced with this function:
(defn xml-to-cass [zipper table key attr & path] (doseq [v (apply zf/xml-> zipper path)] (cass/set-attr! table key attr v)))
EDIT - the next section is beyond the scope of my original question, but any understanding is welcome
The above code is simply the simplest one I could come with to determine my problem. My real code deals with clj-cassandra and zip filters. It may also look super-engineering, but it's just a toy project, and I'm trying to learn a language at the same time.
I want to parse the XML data found on mlb.com and paste the values found in the cassandra database. Here is my code and thinking behind it.
Step 1 - Function that works fine, but contains code duplication
(ns stats.importer (:require [clojure.xml :as xml] [clojure.zip :as zip] [clojure.contrib.zip-filter.xml :as zf] [cassandra.client :as cass])) (def root-url "http://gd2.mlb.com/components/game/mlb/year_2010/month_05/day_01/") (def games-table (cass/mk-cf-spec "localhost" 9160 "mlb-stats" "games")) (defn import-game-xml-1 "Import the content of xml into cassandra" [game-dir] (let [url (str root-url game-dir "game.xml") zipper (zip/xml-zip (xml/parse url)) game-id (.substring game-dir 4 (- (.length game-dir) 1))] (doseq [v (zf/xml-> zipper (zf/attr :type))] (cass/set-attr! games-table game-id :type v)) (doseq [v (zf/xml-> zipper (zf/attr :local_game_time))] (cass/set-attr! games-table game-id :local_game_time v)) (doseq [v (zf/xml-> zipper :team [(zf/attr= :type "home")] (zf/attr :name_full))] (cass/set-attr! games-table game-id :home_team v))))
The import-game-xml-1 parameter can be, for example, "gid_2010_05_01_colmlb_sfnmlb_1/" . I remove the "gid_" and trailing slash to make it the key in ColumnFamily games in my database.
I found that 3 doseq had a lot of duplication (and there should be more than 3 in the final version). So the code templates using the macro seemed appropriate here (correct me if I am wrong).
Step 2 - Presenting the macro for code templates (still working)
(defmacro xml-to-cass [zipper table key attr & path] `(doseq [v# (zf/xml-> ~zipper ~@path )] (cass/set-attr! ~table ~key ~attr v#))) (defn import-game-xml-2 "Import the content of xml into cassandra" [game-dir] (let [url (str root-url game-dir "game.xml") zipper (zip/xml-zip (xml/parse url)) game-id (.substring game-dir 4 (- (.length game-dir) 1))] (xml-to-cass zipper games-table game-id :type (zf/attr :type)) (xml-to-cass zipper games-table game-id :local_game_time (zf/attr :local_game_time)) (xml-to-cass zipper games-table game-id :home_team :team [(zf/attr= :type "home")] (zf/attr :name_full))))
I find that improvement, but I still see some duplication when reusing the same 3 parameters in my xml-to-cass . I introduced an intermediate function to take care of them.
Step 3 - Adding a function to call a macro (problem here)
(defn import-game-xml-3 "Import the content of xml into cassandra" [game-dir] (let [url (str root-url game-dir "game.xml") zipper (zip/xml-zip (xml/parse url)) game-id (.substring game-dir 4 (- (.length game-dir) 1)) save-game-attr (fn[key path] (xml-to-cass zipper games-table game-id key path))] (save-game-attr :type (zf/attr :type)) ; works well because path has only one element (save-game-attr :local_game_time (zf/attr :local_game_time)) (save-game-attr :home :team [(zf/attr= :type "home"] (zf/attr :name_full))))) ; FIXME this final line doesn't work