0

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.

2
  • It looks like something otherthing are ignored here? Commented Jun 10, 2015 at 22:08
  • Yes, much of the macro is already working, I just can't do the loop. Commented Jun 11, 2015 at 0:23

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

I'll see if working with strings and than converting them to symbols will let me do the annotations.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.