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 [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
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 [email protected].
For more options, visit https://groups.google.com/d/optout.