Build macro result with loops - clojure

I'm building a macro that should be called like this:
(myMacro MyController something otherthing
(defn onFoo [this event]
(println "ok"))
(defn onBar [this event]
(println "ok"))
)
After the first three parameters I want to be able to pass a few functions that should be used to build the function definitions in the (definterface and the (deftype part of the macro.
The result of the above call should be this:
(definterface IMyController
(^void onFoo [^javafx.event.ActionEvent event])
(^void onBar [^javafx.event.ActionEvent event])
)
(deftype MyController []
IHandler (^{javafx.fxml.FXML {}} onFoo [this event] (println "ok"))
IHandler (^{javafx.fxml.FXML {}} onBar [this event] (println "ok"))
)
Im very new to Clojure but the hand build implementation of the controller for the FXML file is already working, I just want to simplify it with a macro but I wasn't able to find any help on how do this kind of loop inside a macro definition.
UPDATE
The macro is almost done, and is already running successfully.
(defmacro viewHandler [className & fn-defs]
(def interface (symbol (join ["I" className])))
`(do
(definterface ~interface
~#(for [curr-fn fn-defs]
`(~(second curr-fn) [~'event])
))
(deftype ~className []
~interface
~#(for [curr-fn fn-defs]
(rest curr-fn))
))
)
Called by:
(viewHandler Bar
(defn onFoo [this event] (println "ok-1"))
(defn onBar [this event] (println "ok-2"))
)
But I still can't do the type-hints for the java method annotations.

Start off with something simple:
(defmacro looper [ifc & fn-names]
`(do
~#(for [curr-fn fn-names]
[curr-fn] )))
(println (macroexpand-1
'(looper IFC fun1 fun2 fun3)))
;=> (do [fun1] [fun2] [fun3])
the backquote (backtick?) starts an inline code template. The ~# turns off the template part and starts live code execution (try substituting ~ instead of ~# to see the difference - an extra layer of parentheses).
But, the code we want to output is more like (fun1 [] ...), which is literal code we DON'T want to execute. So, that must be wrapped inside of another syntax-quote/template, but we need another ~ to make the "curr-fn" be "live code" again:
(defmacro looper2 [ifc & fn-defs]
`(do
(definterface ~ifc
~#(for [curr-fn fn-defs]
`(~(second curr-fn) [~'event] )))
(deftype ~ifc []
~#(for [curr-fn fn-defs]
`(IHandler ( ~#(rest curr-fn) [] ))))
))
(newline)
(pprint (macroexpand-1
'(looper2 MyController
(defn fun1 [this event] (println "ok-1"))
(defn fun2 [this event] (println "ok-2"))
)))
Result is:
(do
(clojure.core/definterface MyController
(fun1 [event])
(fun2 [event]))
(clojure.core/deftype
MyController
[]
(basic.t1/IHandler (fun1 [this event] (println "ok-1") []))
(basic.t1/IHandler (fun2 [this event] (println "ok-2") []))))
We need the outer (do ...) form since we are outputting both a (definterface ...) and a (deftype ...) forms.
I'll leave it to you to figure out the type-hints, etc.

Related

how to make correct fn args when create fn using macro

My Clojure app needs some handlers to do business, those handlers will preform some common parameters check, so I use a macro to do this like below:
(defmacro defapihandler [handler-name params & body]
`(defn ~handler-name ~params
(let [keyed-params# (map keyword '~params)
checked-ret# (check-param (zipmap keyed-params# ~params))]
(if (:is-ok checked-ret#)
(do ~#body)
(-> (response {:code 10000
:msg (format " %s are missing !!!" (:missed-params checked-ret#))})
(status 400))))))
Then I can use above macro like this:
(defapihandler create-user [username password birthday]
;; todo
)
Everything is fine this way.
As you can see, the params of generated fn is constructed directly from args of the marco, exception raised when params of generated fn can't constructed directly.
Take a example:
The params of the macro defapihandler now became like this:
[{:key :username :checker [not-nil?]} {:key :password :checkers [is-secure?]} ...]
In the macro, I want to build the param of the generated fn dynamicly like this:
(defmacro defapihandler [handler-name params & body]
`(defn ~handler-name [passed-param#]
(let [param-keys# (vec (map (comp symbol name :key)
~params))
{:keys param-keys#} passed-param#]
;; some check
(do ~#body))))
(defapihandler create-user [{:key :username :checkers []}]
(println username))
The structure of passed-param looks like this: {:username "foo" :password "bar"}
Now I want to construct the variables used in body block in let block, Then following exception is thrown:
Caused by java.lang.IllegalArgumentException
Don't know how to create ISeq from: clojure.lang.Symbol
macroexpand create-user got this:
(defn create-user [passed-param__10243__auto__]
(let [param-keys__10244__auto__ (vec
(map
(comp symbol name :key)
[{:key :username,
:checkers []}]))
{:keys param-keys__10244__auto__} passed-param__10243__auto__]
(do (println username))))
I suspect this exception is related to dynamic var used in let destructuring form, if my suspect is right, then how to construct variables used in body block ?
You need to pull the clause that builds your params-key vector out of the generated code.
So:
(defmacro defapihandler [handler-name params & body]
(let [param-keys (map (comp symbol name :key) params)]
`(defn ~handler-name [passed-param#]
(let [{:keys [~#param-keys]} passed-param#]
;; some check
(do ~#body)))))
Or if you don't need passed-param#:
(defmacro defapihandler [handler-name params & body]
(let [param-keys (map (comp symbol name :key) params)]
`(defn ~handler-name [{:keys [~#param-keys]}]
;; some check
(do ~#body))))

Clojure - close over a function in another namespace

Let's say that I have the following :
(defn my-fn [params]
(make-things (a-fn [ctx]
(do-this params)))
Now I wish to split this into different files, such that a-fn is in another namespace :
(defn my-fn [params]
(make-things my.ns/a-fn))
But the problem now is that the params doesn't close over my function anymore. How should do that ?
Rewrite:
(defn my-fn [params]
(make-things (fn a-fn [ctx]
(do-this params)))
into
(defn my-fn [params]
(make-things ((fn a-fn-o [p]
(fn a-fn [ctx]
(do-this p)))
params)))
This is what the compiler does for you when you close over variables.
Then it should be clear what to do in your other file. The function returns a function and you pass in params:
(defn my-fn [params]
(make-things (my.ns/a-fn params)))
;; my.ns
(defn a-fn [params]
(fn [ctx] (do-this params)))
Btw, your ctx parameter is unused.
Just so that's mentioned too, you might instead of closures also want to consider using a Var to scope the params dynamically rather than lexically. This is especially useful if they're "implicit arguments" to several related functions in my.ns.
(ns my.ns)
(def ^:dynamic *params* ...) ;;Optional default value here
(defn a-fn [...]
(do-this *params* ...))
and then in the calling ns
(defn my-fn [params]
(binding [my.ns/*params* params]
(my.ns/a-fn ...)))
This is how for instance with-out-str alters the behaviour of any print variant both in its body and in subcomputations - by rebinding *out* to an empty string writer.
You could also use partial:
(ns my.ns)
(defn a-fn [params ctx]
(do-this params))
Then in the calling context:
(defn my-fn [params]
(make-things (partial my.ns/a-fn params)))

Attempt to add annotation to defrecord defined class in macro

I'm attempting to create a macro similar to the Quartzite defjob macro that creates the Job class with the #DisallowConcurrentExecution annotation added to it. The code works from the repl, but not inside the macro.
This works...
user=> (defrecord ^{DisallowConcurrentExecution true} YYY []
#_=> org.quartz.Job
#_=> (execute [this context]
#_=> (println "whoosh!")))
user.YYY
user=> (seq (.getAnnotations YYY))
(#<$Proxy3 #org.quartz.DisallowConcurrentExecution()>)
...but this does not.
(defmacro defncjob
[jtype args & body]
`(defrecord ^{DisallowConcurrentExecution true} ~jtype []
org.quartz.Job
(execute [this ~#args]
~#body)))
After Rodrigo's suggestion, here is a way to make it work.
(defmacro defdcejob
[jtype args & body]
`(defrecord ~(vary-meta jtype assoc `DisallowConcurrentExecution true) []
org.quartz.Job
(execute [this ~#args]
~#body)))
You can't use the ^ reader macro inside macros. Take a look at this similar question.

How to do hooks in Clojure

I have a situation where I am creating and destroying objects in one clojure namespace, and want another namespace to co-ordinate. However I do not want the first namespace to have to call the second explicitly on object destruction.
In Java, I could use a listener. Unfortunately the underlying java libraries do not signal events on object destruction. If I were in Emacs-Lisp, then I'd use hooks which do the trick.
Now, in clojure I am not so sure. I have found the Robert Hooke library https://github.com/technomancy/robert-hooke. But this is more like defadvice in elisp terms -- I am composing functions. More over the documentation says:
"Hooks are meant to extend functions you don't control; if you own the target function there are obviously better ways to change its behaviour."
Sadly, I am not finding it so obvious.
Another possibility would be to use add-watch, but this is marked as alpha.
Am I missing another obvious solution?
Example Added:
So First namespace....
(ns scratch-clj.first
(:require [scratch-clj.another]))
(def listf (ref ()))
(defn add-object []
(dosync
(ref-set listf (conj
#listf (Object.))))
(println listf))
(defn remove-object []
(scratch-clj.another/do-something-useful (first #listf))
(dosync
(ref-set listf (rest #listf)))
(println listf))
(add-object)
(remove-object)
Second namespace
(ns scratch-clj.another)
(defn do-something-useful [object]
(println "object removed is:" object))
The problem here is that scratch-clj.first has to require another and explicitly push removal events across. This is a bit clunky, but also doesn't work if I had "yet-another" namespace, which also wanted to listen.
Hence I thought of hooking the first function.
Is this solution suitable to your requirements?
scratch-clj.first:
(ns scratch-clj.first)
(def listf (atom []))
(def destroy-listeners (atom []))
(def add-listeners (atom []))
(defn add-destroy-listener [f]
(swap! destroy-listeners conj f))
(defn add-add-listener [f]
(swap! add-listeners conj f))
(defn add-object []
(let [o (Object.)]
(doseq [f #add-listeners] (f o))
(swap! listf conj o)
(println #listf)))
(defn remove-object []
(doseq [f #destroy-listeners] (f (first #listf)))
(swap! listf rest)
(println #listf))
Some listeners:
(ns scratch-clj.another
(:require [scratch-clj.first :as fst]))
(defn do-something-useful-on-remove [object]
(println "object removed is:" object))
(defn do-something-useful-on-add [object]
(println "object added is:" object))
Init binds:
(ns scratch-clj.testit
(require [scratch-clj.another :as another]
[scratch-clj.first :as fst]))
(defn add-listeners []
(fst/add-destroy-listener another/do-something-useful-on-remove)
(fst/add-add-listener another/do-something-useful-on-add))
(defn test-it []
(add-listeners)
(fst/add-object)
(fst/remove-object))
test:
(test-it)
=> object added is: #<Object java.lang.Object#c7aaef>
[#<Object java.lang.Object#c7aaef>]
object removed is: #<Object java.lang.Object#c7aaef>
()
It sounds a lot like what you're describing is callbacks.
Something like:
(defn make-object
[destructor-fn]
{:destructor destructor-fn :other-data "data"})
(defn destroy-object
[obj]
((:destructor obj) obj))
; somewhere at the calling code...
user> (defn my-callback [o] (pr [:destroying o]))
#'user/my-callback
user> (destroy-object (make-object my-callback))
[:destroying {:destructor #<user$my_callback user$my_callback#73b8cdd5>, :other-data "data"}]
nil
user>
So, here is my final solution following mobytes suggestion. A bit more work, but
I suspect that I will want this in future.
Thanks for all the help
;; hook system
(defn make-hook []
(atom []))
(defn add-hook [hook func]
(do
(when-not
(some #{func} #hook)
(swap! hook conj func))
#hook))
(defn remove-hook [hook func]
(swap! hook
(partial
remove #{func})))
(defn clear-hook [hook]
(reset! hook []))
(defn run-hook
([hook]
(doseq [func #hook] (func)))
([hook & rest]
(doseq [func #hook] (apply func rest))))
(defn phils-hook []
(println "Phils hook"))
(defn phils-hook2 []
(println "Phils hook2"))
(def test-hook (make-hook))
(add-hook test-hook phils-hook)
(add-hook test-hook phils-hook2)
(run-hook test-hook)
(remove-hook test-hook phils-hook)
(run-hook test-hook)

How to expand a sequence (var-args) into distinct items

I want to send var-args of a function to a macro, still as 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 output of (test-macro "a" "b" "c") is what I want: count=3; args=abc
The output of (test-fn-calling-macro "a" "b" "c") is : count=1; args=("a" "b" "c") because args is sent as a single argument to the macro. How can I expand this args in my function in order to call the macro with the 3 arguments?
I guess I'm just missing a simple core function but I'm not able to find it. Thanks
EDIT 2 - My "real" code, shown in EDIT section below is not a valid situation to use this technique.
As pointed out by #Brian, the macro xml-to-cass can be replaced with a function like this:
(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 following section goes beyond my original question but any insight is welcome
The code above is just the most simple I could come with to pinpoint my problem. My real code deals with clj-cassandra and zip-filter. It may also look over-engineering but it's just a toy project and I'm trying to learn the language at the same time.
I want to parse some XML found on mlb.com and insert values found into a cassandra database. Here is my code and the thinking behind it.
Step 1 - Function which 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 parameter to import-game-xml-1 can be for example "gid_2010_05_01_colmlb_sfnmlb_1/". I remove the "gid_" and the trailing slash to make it the key of the ColumnFamily games in my database.
I found that the 3 doseq were a lot of duplication (and there should be more than 3 in the final version). So code templating using a macro seemed appropriate here (correct me if I'm wrong).
Step 2 - Introducing a macro for code templating (still works)
(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 believe that's an improvement but I still see some duplication in always reusing the same 3 parameters in my calls to xml-to-cass. That's were I introduced an intermediate function to take care of those.
Step 3 - Adding a function to call the macro (the problem is 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
Here's a some simple code which may be illuminating.
Macros are about code generation. If you want that to happen at runtime, for some reason, then you have to build and evaluate the code at runtime. This can be a powerful technique.
(defmacro test-macro
[& args]
`(println (str "count=" ~(count args) "; args=" ~#args)))
(defn test-fn-calling-macro
[& args]
(test-macro args))
(defn test-fn-expanding-macro-at-runtime
[& args]
(eval (cons `test-macro args)))
(defmacro test-macro-expanding-macro-at-compile-time
[& args]
(cons `test-macro args))
;; using the splicing notation
(defmacro test-macro-expanding-macro-at-compile-time-2
[& args]
`(test-macro ~#args))
(defn test-fn-expanding-macro-at-runtime-2
[& args]
(eval `(test-macro ~#args)))
(test-macro "a" "b" "c") ;; count=3; args=abc nil
(test-fn-calling-macro "a" "b" "c") ;; count=1; args=("a" "b" "c") nil
(test-fn-expanding-macro-at-runtime "a" "b" "c") ; count=3; args=abc nil
(test-macro-expanding-macro-at-compile-time "a" "b" "c") ; count=3; args=abc nil
(test-macro-expanding-macro-at-compile-time-2 "a" "b" "c") ; count=3; args=abc nil
(test-fn-expanding-macro-at-runtime "a" "b" "c") ; count=3; args=abc nil
If contemplation of the above doesn't prove enlightening, might I suggest a couple of my own blog articles?
In this one I go through macros from scratch, and how clojure's work in particular:
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-i-getting.html
And in this one I show why run-time code generation might be useful:
http://www.learningclojure.com/2010/09/clojure-faster-than-machine-code.html
The typical way to use a collection as individual arguments to a function is to use (apply function my-list-o-args)
(defn test-not-a-macro [& args]
(print args))
(defn calls-the-not-a-macro [& args]
(apply test-not-a-macro args))
though you wont be able to use apply because test-macro is a macro. to solve this problem you will need to wrap test macro in a function call so you can apply on it.
(defmacro test-macro [& args]
`(println ~#args))
(defn calls-test-macro [& args]
(eval (concat '(test-macro) (args)))) ;you almost never need eval.
(defn calls-calls-test-macro [& args]
(calls-test-macro args))
This is actually a really good example of one of the ways macros are hard to compose. (some would say they cant be composed cleanly, though i think thats an exageration)
Macros are not magic. They are a mechanism to convert code at compile-time to equivalent code; they are not used at run-time. The pain you are feeling is because you are trying to do something you should not be trying to do.
I don't know the library in question, but if cass/set-attr! is a function, I see no reason why the macro you defined has to be a macro; it could be a function instead. You can do what you want to do if you can rewrite your macro as a function instead.
Your requirements aren't clear. I don't see why a macro is necessary here for test-macro, unless you're trying to print the unevaluated forms supplied to your macro.
These functions provide your expected results, but that's because your sample data was self-evaluating.
(defn test-args
[& args]
(println (format "count=%d; args=%s"
(count args)
(apply str args))))
or
(defn test-args
[& args]
(print (format "count=%d; args=" (count args)))
(doseq [a args]
(pr a))
(newline))
You can imagine other variations to get to the same result.
Try calling that function with something that doesn't evaluate to itself, and note the result:
(test-args (+ 1 2) (+ 3 4))
Were you looking to see the arguments printed as "37" or "(+ 1 2)(+ 3 4)"?
If you were instead trying to learn about macros and their expansion in general, as opposed to solving this particular problem, please tune your question to probe further.