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.

Reply via email to