-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
added @macroexpand
#18660
added @macroexpand
#18660
Conversation
@@ -61,6 +61,16 @@ Takes the expression `x` and returns an equivalent expression with all macros re | |||
""" | |||
macroexpand(x::ANY) = ccall(:jl_macroexpand, Any, (Any,), x) | |||
|
|||
""" | |||
@macroexpand |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing indent, and would have to be added to rst if this gets merged before #18588
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where do I have to add it to rst?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@test (@macroexpand @doc "" f() = @x) == Expr(:error, UndefVarError(Symbol("@x"))) | ||
@test (@macroexpand @seven_dollar $bar) == 7 | ||
x = 2 | ||
@test (@macroexpand @seven_dollar 1+$x) == :(1 + 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this actually the correct behaviour? Shouldn't it throw an error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right!
macro macroexpand(code)
code_expanded = macroexpand(code)
Expr(:quote, code_expanded)
end
code_expanded looks good the problem is the eval call on Expr(:quote, code_expanded) which does $ interpolation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So would the solution be to search and quote the dollars expressions in code_expanded?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm honestly not sure. This stuff always makes my head hurt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think correct behaviour would be not to throw an error but return something like
1 + $(Expr(:$, :x))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah my head hurts, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it would be better to print the result, rather than return it, i.e. do:
macro macroprint(code)
print(macroexpand(code))
end
After all, that's probably what you want in 99% of cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👎 to print it.
macro macroexpand(code)
QuoteNode(macroexpand(code))
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is eval(QuoteNode(ex)) == ex
for all expressions ex?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be.
LGTM |
@@ -0,0 +1,18 @@ | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this in a new file? This can probably go to parse
or replutil
or reflection
.
""" | ||
@macroexpand | ||
|
||
Return equivalent expression with all macros removed (expanded). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be documented that the expansion happens in the module where the @macroexpand
macro is expanded (i.e. the same module if the expression is used directly) and not the current module when the returned code runs (which is the module if you call macroexpand
) The difference is demonstrated below
julia> module M
macro macroexpand(code)
QuoteNode(macroexpand(code))
end
macro m()
1
end
function f()
(@macroexpand(@m), macroexpand(:(@m)))
end
end
M
julia> macro m()
2
end
@m (macro with 1 method)
julia> M.f()
(1,2)
This makes no difference when used in the REPL and as I said the current @macroexpand
might be slightly better but this should still be documented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you give a usecase this would actually make a difference? And why is @macroexpand
potentially better for interactive use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you give a usecase this would actually make a difference?
That's exactly what the code above is showing.
And why is
@macroexpand
potentially better for interactive use?
I said
This makes no difference when used in the REPL
LGTM other than the comments above. |
commit messages could also be a bit more descriptive |
The PR title and the first commit message looks OK (more detail is fine but it's not bad). I assume the fix up commits will be squashed when merged. |
So this needs a squash and then can be merged? Is that correct, @yuyichao? |
Also #18660 (review) |
julia> M.f() | ||
(1,2) | ||
``` | ||
With @macroexpand the expression expands where @macroexpand appears in the code (module M). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@macroexpand
, macroexpand
and M
should be quoted. Otherwise 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Will merge later after the CI finishes. Thx for the contribution.
Cool! |
With macroexpand the expressions expands in the current module where the code was finally called. | ||
Note that when calling macroexpand or @macroexpand directly from the REPL, both of these contexts coincide, hence there is no difference. | ||
With `@macroexpand` the expression expands where `@macroexpand` appears in the code (module `M`). | ||
With `macroexpand` the expressions expands in the current module where the code was finally called (REPL). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this isn't the repl if the function is run in a script, is it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me explain it differently. If you call macroexpand
, the expansion takes place at runtime. If you call @macroexpand
the expansion takes place at compile time (when @macroexpand
is expanded to be more precise.). So the context of @macroexpand
is where it appears in the code, while the context of macroexpand
is the current environment. So if you run M.f() from a script, the answer will be
(1, whatever @m() currently means in your script)
Does this answer the question?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense. I'm referring to the "where the code was finally called (REPL)." here. I don't see why "(REPL)" is accurate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But in the example it is the REPL?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're making a general statement here "with macroexpand
the expression expands` ... followed by a very not-general parenthetical that doesn't serve much purpose
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The julia>
in the example implies that it runs in the REPL, doesn't it? And the "(module M
)" in the sentence before also makes sense only in context of the example. So I'd say either leave as is or rephrase as "(module M
in the example)" and "(REPL in the example)" (which I could well do in #18784).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@martinholters I am happy with either formulation, so rephrase it as you see fit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a few words saying "in the example" sounds like a good clarification, thanks
Fix #18240