1

I'm using petrol for a clojurescript application, and I've found a lot of boilerplate is necessary when extending protocols with messages.

So I decided to write a macro! Now, if only it worked :/

(ns my.macros)

(defmacro state-message [msg-type app-key]
   `(extend-protocol
      Message
      ~msg-type
      (process-message [msg app]
         (let [state (app-key app)]
           (->>
             (merge state msg)
             (assoc app app-key))))))

The code above is approximately what I'd like to write, but I'm having some trouble getting the quoting right. Rather than doing what I intended, it's giving me a compiler error Invalid local name: ohds.macros/msg when I try to use it like (state-message m/ChangeUsername :user)

Macroexpand gives me this, and it looks right:

(clojure.core/extend-protocol 
    ohds.server/Message
    m/ChangeUsername 
    (ohds.server/process-message [ohds.server.msg ohds.server/app]    
        (clojure.core/let [ohds.server/state (ohds.server/app-key ohds.server/app)] 
            (clojure.core/->> 
                (clojure.core/merge ohds.server/state ohds.server.msg) 
                (clojure.core/assoc ohds.server/app ohds.server/app-key)))))

I suspect the problem with with msg but I'm not sure how to proceed.

munk
  • 12,340
  • 8
  • 51
  • 71

1 Answers1

1

You need to use gensyms for introducing new local bindings in your macro. The following should work:

(defmacro state-message [msg-type app-key]
 `(extend-protocol
    Message
    ~msg-type
    (~'process-message [msg# app#]
       (let [state# (~app-key app#)]
         (->>
           (merge state# msg#)
           (assoc app# ~app-key))))))
anmonteiro
  • 210
  • 5
  • 9