Hi 박상규, The trick with ClojureScript defmacro is that it behaves differently in different circumstances.
If you call defmacro in a cljs file, it actually defines a function, not a macro, instead of throwing an error. I find that pretty confusing. The function will produce the form instead of expanding the form like a macro would. There is no error reported. A cljc file can be loaded in response to either :require-macros, :require, or being in your compilation directory. If it gets loaded by :require-macros, it will define the macro correctly. If it gets loaded by :require, it will define the macro incorrectly. In your example, you never :require the file, but the file is compiled never the less, because it is in the src directory. What this does is produces the incorrect function. However because you ALSO :require-macros, it also correctly creates the macro. Now there is a weird situation where if you refer to the macro using the fully qualified namespace, it will call the incorrect function, but if you just call the macro as you have, it works. So to answer your question, yes in your example the (defmacro) happened twice: Once in the correct context of defining the macro in clj, and a second time in the incorrect context of defining a function in cljs. It is confusing and bad to have (defmacro) called in both contexts. You can avoid this however by wrapping your macro in #?(:clj (defmacro ...)) Now it will only get defined once, in the correct context. You can find more detailed examples here: http://blog.fikesfarm.com/posts/2015-06-19-portable-macro-musing.html https://stackoverflow.com/questions/29914967/can-cljc-single-file-macro-definitions-to-work-with-clojurescript But the easiest thing to do to avoid confusion is to only put your macros in a clj file, not a cljc file. Regards, Timothy On Friday, September 15, 2017 at 11:27:24 AM UTC-7, 박상규 wrote: > > oh, sorry for my unclear questions. > > In my thought, the following steps might be expected when ClojureScript > compiler compiles core.cljs above. > > 1) It meets `ns` form at line 1 in core.cljs, handles `:require-macros`, > and it tries to load macro.cljc as Clojure code because what is required is > macros as the keyword `:require-macros` means > > 2) Loading macro.cljc, as expected, it makes a namespace named 'macro' and > registers symbols and vars to use for compile time, not for runtime. > > 3) At line 6 in core.cljs it meets `log` form and it tries to macro-expand > the `log` macro. > > 4) It finds the `log` macro var in macro namespace which is registered > just before, call the `log` macro var to expand the `log` form, and is > returned the expanded form which includes `now` symbol. > > 5) At this time ClojureScript compiler starts to resolve the symbols in > the expanded form. > > 6) And It meets symbol `now` and searches in all namespaces to resolve the > symbol to use for runtime. > > 7) When the modified macro.cljc above is used, ClojureScript compiler > can't resolve the symbol `now` for runtime, because the `now` function is a > macro helper function to be used for compile time. Hence the warning. > > 8) But when the unmodified macro.cljc above is used, ClojureScript > compiler emited no warning. This means ClojureScript compiler resolved the > symbol `now` for runtime, which I thought must be registered by loading > macro.cljc as ClojureScript code. > > So, this makes me have a doubt that ClojureScript compiler load macro.cljc > twice, one time as ClojureScript code, next time as Clojure code. > > > 2017년 9월 15일 금요일 오후 9시 40분 19초 UTC+9, David Nolen 님의 말: >> >> You cannot define macros in .cljs files or .cljc files you intend to load >> as ClojureScript. So it's not clear to me what you are trying to >> demonstrate. >> >> HTH, >> David >> >> On Thu, Sep 14, 2017 at 4:37 AM, 박상규 <[email protected]> wrote: >> >>> I have wondered how clojurescript compiler processes macros. >>> So I tested as following: >>> >>> I made dirs and files as below. >>> >>> guruma@mac cljs $ tree . >>> . >>> ├── build.clj >>> ├── cljs.jar >>> └── src >>> └── foo >>> ├── core.cljs >>> └── macro.cljc >>> >>> 2 directories, 4 files >>> >>> I downloaded cljs.jar using curl. >>> >>> guruma@mac cljs $ curl -LOk >>> https://github.com/clojure/clojurescript/releases/download/r1.9.908/cljs.jar >>> >>> And I made build.clj, core.cljs and macro.cljc as below. >>> >>> build.clj >>> (require 'cljs.build.api) >>> >>> (cljs.build.api/build >>> "./src" >>> {:main 'core >>> :output-to "out/main.js"}) >>> >>> core.cljs >>> (ns foo.core >>> (:require-macros [macro :refer [log]])) >>> >>> (enable-console-print!) >>> >>> (log "hello, world") >>> >>> core.cljs require macro.cljc and use a macro 'log'. >>> >>> macro.cljc >>> (ns foo.macro) >>> >>> (defn now [] >>> #?(:clj (System/currentTimeMillis) >>> :cljs (js/Date.now))) >>> >>> (defmacro log [x] >>> `(println "log[" (now) "]: " ~x)) >>> >>> macro.cljc defines a macro 'log'. >>> And there is the helper function 'now' that has read-conditionals in its >>> body. >>> >>> And compile. >>> >>> guruma@mac cljs $ java -cp cljs.jar:src clojure.main build.clj >>> guruma@mac cljs $ >>> >>> No error and no warning. >>> >>> And I modified macro.cljc as below >>> >>> macro.cljc modified >>> (ns foo.macro) >>> >>> #?(:clj (defn now [] ; <== modified >>> (System/currentTimeMillis))) >>> >>> (defmacro log [x] >>> `(println "log[" (now) "]: " ~x)) >>> >>> the function 'now' is in read-conditional. >>> >>> And compile. >>> >>> guruma@mac cljs $ java -cp cljs.jar:src clojure.main build.clj >>> WARNING: Use of undeclared Var foo.macro/now at line 6 >>> ./src/foo/core.cljs >>> >>> Here is a warning and it is out of question because there is no >>> definition of the function 'now' for clojurescript to be used for runtime. >>> >>> But what i can't understand is why there is no warning when compiling >>> unmodified macro.cljc. >>> >>> My questions are: >>> >>> 1) Does clojurescript compiler load macro twice for :clj and :cljs in >>> read-conditional when handling :require-macros? >>> 2) How does clojurescript compiler resolve the symbol 'now' defined in >>> macro.cljc? >>> >>> Thanks a lot in advance. >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> -- >>> Note that posts from new members are moderated - please be patient with >>> your first post. >>> --- >>> You received this message because you are subscribed to the Google >>> Groups "ClojureScript" group. >>> To unsubscribe from this group and stop receiving emails from it, send >>> an email to [email protected]. >>> To post to this group, send email to [email protected]. >>> Visit this group at https://groups.google.com/group/clojurescript. >>> >> >> -- Note that posts from new members are moderated - please be patient with your first post. --- You received this message because you are subscribed to the Google Groups "ClojureScript" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at https://groups.google.com/group/clojurescript.
