2
votes

In Clojure, the branch of an if expression that doesn't match the condition is not evaluated, so no exception is thrown in evaluating the below expression:

=> (if nil (/ 1 0))
nil

However, macros will still be expanded before evaluating the if, so you can still get exceptions like so:

=> (if nil (proxy [nil] []))
CompilerException java.lang.NullPointerException, compiling:(NO_SOURCE_PATH:1)

I'm writing a macro for which the name of an interface will sometimes be provided as an arg, in which case the macro will generate the code for a call to proxy. Other times, the interface arg will be nil, and then the macro expands to something like (if nil (proxy [nil] []), so I get the above exception. An SSCE would be:

=> (defmacro make-listener [listener-name & methods]
     `(if ~listener-name
        (proxy [~listener-name] []
          ~@(map (fn [m] `(~m [e#])) methods))))
#'user/make-listener
=> (make-listener java.awt.event.ActionListener actionPerformed)
#<Object$ActionListener$46793e3a user.proxy$java.lang.Object$ActionListener$46793e3a@ed5b2>
=> (make-listener nil)
CompilerException java.lang.NullPointerException, compiling:(NO_SOURCE_PATH:1) 

How can I write the macro in a way that doesn't throw an exception when no interface arg is provided?

1

1 Answers

4
votes

If listener-name is nil then the macro should do nothing i.e you need to check for listener-name at macro expansion (not in the code the macro emit).

(defmacro make-listener [listener-name & methods]
  (if listener-name
     `(proxy [~listener-name] []
       ~@(map (fn [m] `(~m [e#])) methods))))