The compiler seems to currently use the *:const* hint, to know that it should return the analyzed expression value for the var (as if it was quoted) instead of the *VarExpr* (which would mean a deref at run-time) (see here <https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L7320> ).
For the code you posted, my understanding of what the compiler does is: (def ^:const bar 100) ;; `bar` is created with {:const true} metadata attached to it. (defmacro foo [x] (+ 10 x)) ;; `foo` macro is created with the function body provided. (foo bar) The list expression `(foo bar)` is identified as a seq so it's analyzed as one here <;; https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L7084> . This tries to macroexpand the expression, which in this case will result in `(foo bar)` being expanded into `(+ 10 foo)`. This new expression `(+ 10 foo)` gets analyzed as a seq again, but on macroexpansion there's nothing to do, so it's unchanged. There are some checks for the first form in the seq which include: 1. Is it `nil`? 2. Is it a var (or does it resolve to a var) with `:inline` metadata specified which applies to the current number of args (here <https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L6922> )? 3. Is it the symbol `fn*`? 4. Is it any of the special forms? 5. Otherwise consider it as a function invocation expression. In our example the `+` symbol resolves to the function `clojure.core/+` which does have `:inline` metadata for inlining. The inlining works sort of like macroexpansion and the `(+ 10 foo)` expression will end up being `(. clojure.lang.Numbers (add 10 foo))` (see here <https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L987> and here <https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L955> ). This expression gets analyzed as a seq as well. The `.` is a special form (see here <https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L118>), but that's not important. What we care about at this point is that the compiler hasn't tried to resolved the symbol `foo` so far, it will try to do so while parsing this DOT expression here <https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L1019>. This will finally result in the `:const` metadata entry being used in here <https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L7320> and the constant expression being returned. So what we end up with is the equivalent to analyzing the expression `(. clojure.lang.Numbers (add 10 100))`. The JVM probably includes constant folding optimization, but I doubt it's able to figure out that `clojure.lang.Number/add` <https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Numbers.java#L152> is number addition. I hope this helps in somw way. It was a fun exercise. :) Cheers! On Monday, November 5, 2018 at 12:07:44 AM UTC+1, Didier wrote: > > Macroexpansion is given the raw forms that are read, and I think >> that's a good thing. Inside a macro you can always call >> macroexpand yourself if you want to (or resolve values another >> way) but there's no way to "undo" automatic macroexpansion. >> > > That's a good point. I guess resolving the constant inside the macro still > performs the compile time optimization of in-lining. > > I guess my question now becomes, when does const actually expands? Or what > does it do exactly? In my mind, it is supposed to inline its usage. So does > it do so after expansion? > > On Thursday, 15 March 2018 18:15:16 UTC-7, Carlo Zancanaro wrote: >> >> On Thu, Mar 15 2018, Didier wrote: >> > I feel it would be really cool to be able to factor some input >> > to macros into constants, is this something I could open a >> > ticket for, to extend ^:const so it can inline all literal >> > values and also does the inlining before macroexpanssion so that >> > the above would work? >> >> Macroexpansion is given the raw forms that are read, and I think >> that's a good thing. Inside a macro you can always call >> macroexpand yourself if you want to (or resolve values another >> way) but there's no way to "undo" automatic macroexpansion. >> >> As one particular example, how would this work in your world? If >> the `bar` is replaced ahead of time in the usage of `l` then it >> cannot be shadowed as an identifier. >> >> (def ^:const bar {:a 100}) >> (defmacro l [name value & body] >> `(let [~name ~value] >> ~@body)) >> (l bar 1 >> (+ bar 1)) >> >> If you want to factor out things for macros (particularly macros >> that you don't control) you can try doing so at read time, with >> something that evaluates at read time, like so: >> >> (def bar {:a 100}) >> (defmacro foo [x] >> (:a x)) >> (foo #=(eval bar)) ;; => 100 >> >> It's a bit messy, but it draws attention to the fact that there's >> something unusual happening with evaluation order. >> >> Carlo >> > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.