3
votes

I have the following code in a clj (clojure) namespace.

(ns project.clojure.clojurescript-macros)

(def trace-history (atom []))

; hmm, you could run out of memory on recursive functions here?
; Fortunately functions aren't actually recursive in clojure. :]
(defmacro push-args [name args]
  `(swap! trace-history
     (fn [stack#]
       (conj stack# [~name (zipmap ~(vec (map str args)) ~args)]))))

(push-args :hello [:a :b :c])

within another cljs (clojurescript) namespace, I have the following

(ns project.clojurescript.user   
  (:require-macros [project.clojure.clojurescript-macros :as c]))

(c/push-args :hello [:a :b :c])

I compile my clojurescript code, and open it in my browser, Unfortunately, I get the following error.

Uncaught TypeError: Cannot read property 'trace_history' of undefined main.js:22348
(anonymous function)                                                  main.js:22348

Looking at line 22348 in my compiled clojurescipt code, I see the following.

cljs.core.swap_BANG_.call(null, project.clojure.trace_history, function(stack__6402__auto__) {
  return cljs.core.conj.call(null, stack__6402__auto__, 
    cljs.core.PersistentVector.fromArray(["\ufdd0'hello", 
      cljs.core.zipmap.call(null, 
        cljs.core.PersistentVector.fromArray([":a", ":b", ":c"], true),          
        cljs.core.PersistentVector.fromArray(["\ufdd0'a", "\ufdd0'b", "\ufdd0'c"], 
      true))], 
    true))
  });

The problem is that project.clojure.trace_history has not been defined anywhere in main.js . I understand what is wrong, but I am unsure as to how I might fix it. I have tried out other solutions, such as putting trace-history in a shared clojure file and putting trace history in a cljs file itself. None seem to work. Given that I want to have a shared global atom between all compilations of this macro, how can I do so in Clojurescript?

1
Just curious, why is push-args a macro? I don't see it doing anything that a function couldn't do just as well with the same syntax, and macros are a lot harder to reason about, particularly in ClojureScript. - levand
In my code, args are actually the argument list passed to my custom defn macro (wich mostly just calls to defn). I am attempting to conj a [fx-name {map of fx-names arguments/values}] into the trace-history variable every time one of my custom defn functions is called (and pop the value off when the function is successfully run). Then, whenever a assertion fails, I can print the trace-history and hopefully diagnose why the assertion failed more easily. Similar to the debugger, but available whenever assertions are enabled. - Stephen Cagle

1 Answers

3
votes

An atom is run-time data, a macro is a compile-time construct. It doesn't really make sense for a macro to have a "shared global atom" - what your macro there does is compile to code which simply expects there to be a symbol called trace-history in the current NS.

If you simply want to write common code which can use an atom, then you should write it such that can take the atom as an argument rather than assuming its existence via a hardcoded symbol name.

If you mean you want the same atom's value to be available to you on both the client and server of a Clojure+ClojureScript application, you'll need to write your own code to coordinate the value via Ajax calls... that's not something built in to the language.