1

As a follow-up to my previous question, I am trying to write a macro that builds a defprotocol:

(build-protocol AProtocol
  [(a-method [this]) (b-method [this that])]
  (map (fn [name] `(~(symbol (str name "-method")) [~'this ~'that ~'the-other]))
    ["foo" "bar" "baz"])
  (map (fn [name] `(~(symbol (str name "-method")) [~'_]))
    ["hello" "goodbye"]))

should expand to

(defprotocol AProtocol
  (a-method [this])
  (b-method [this that])
  (foo-method [this that the-other])
  (bar-method [this that the-other])
  (baz-method [this that the-other])
  (hello-fn [_])
  (goodbye-fn [_]))

My attempt:

(defmacro build-protocol [name simple & complex]
  `(defprotocol ~name ~@simple
     ~@(loop [complex complex ret []]
         (if (seq complex)
           (recur (rest complex) (into ret (eval (first complex))))
           ret))))

and expansion (macroexpand-1 '(...)):

(clojure.core/defprotocol AProtocol
  (a-method [this])
  (b-method [this that])
  (foo-method [this that the-other])
  (bar-method [this that the-other])
  (baz-method [this that the-other])
  (hello-method [_])
  (goodbye-method [_]))

I'm not really happy about the eval. Also, the map expressions are pretty ugly. Is there a better way? Any and all comments welcome.

Once I get this working, I'm going to do a similar macro for (build-reify ...). I'm writing a rather large Swing application and have several components (JButtons, JCheckBoxes, etc.) that have almost identical method signatures and actions.

1 Answer 1

2

I think you're doing it upside down. Specify the "-method" stuff first, wrapped in a container of some kind so build-protocol knows what's what, and let it do the map inside the macro. e.g:

(build-protocol AProtocol
  {[this that whatever] [foo bar baz],
   [_] [hello goodbye]}
  ; a-method and b-method...
)
Sign up to request clarification or add additional context in comments.

5 Comments

Interesting idea. I'll have to explore it.
See, for example, github.com/amalloy/ordered/blob/develop/src/deftype/… - I wrote this over the weekend and checked it in this morning. It defines a new delegating-deftype (seen in use at github.com/amalloy/ordered/blob/develop/src/ordered/map.clj), in much the same way I recommended you do build-protocol.
@Ralph I went ahead and implemented a rough draft of this for you so you can see what I mean: github.com/amalloy/build-protocol/blob/develop/src/…
I looked at your example at github.com/amalloy/build-protocol/blob/develop/src/…. I like it. Thanks.
I am getting ready to try a (grid-bag-panel...) following the example Stuart Sierra did in his blog (stuartsierra.com/2010/01/05/taming-the-gridbaglayout), but I have some ideas of my own. The best way to learn this stuff is to actually do it (reading only goes so far).

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.