munk
munk

Reputation: 12983

Symbol is associated with the wrong namespace in a macro

I have this macro:

(defmacro widget [msg-type value & app-key]
  `(defrecord ~msg-type [~value]
     Message
     (~'process-message [msg# app#]
      (let [state# (~@app-key app#)]
        (dissoc
         (->>
          (merge state# msg#)
          (assoc app# ~@app-key))
         :errors)))))

Message is a protocol defined in a clojurescript dependency, with a process-message function.

When I try to use widget like so

(ns my.cljs.ns
  (:require-macros [my.ns.macros :as macro])
  (:require [petrol.core :refer [Message]]))

(macro/widget A-Record a-field :a-key)

I get this error:

Bad method signature in protocol implementation, 
my.ns.macros/Message does not declare method called 
process-message ...

How can I get Message to refer to petrol/Message instead of my.ns.macros/Message?

Upvotes: 2

Views: 103

Answers (1)

Timothy Pratley
Timothy Pratley

Reputation: 10662

You need the power of the mystical ~' operator :) I see you already invoked it for process-message, so perhaps you are already acquainted with why; but for the purposes of the answer, stuff in the backtick gets fully namespace qualified, where as evaluate quote puts the literal symbol in place.

(macroexpand-1 '(widget :a :b))

And the error message indicate that you need to ~'Message if you want to avoid it having the current ns attached to it.

However fully qualifying Message with the petrol namespace would be a good move IMO

petrol.core/Message

That way you don't need to rely on it being referred in the ns declaration. Note you don't need to ~' it either.

Also I would be wary of (~@app-key app#) because app-key are optional... you could get nothing passed in which would call whatever #app is, which doesn't sound like something you want to happen. Similarly passing more than one seems wierd to. Maybe it should be a required param?

Upvotes: 2

Related Questions