Skip to content
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

Merged
merged 8 commits into from Sep 30, 2016
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions base/exports.jl
Expand Up @@ -1052,6 +1052,7 @@ export
expand,
gensym,
macroexpand,
@macroexpand,
parse,

# help and reflection
Expand Down
36 changes: 36 additions & 0 deletions base/expr.jl
Expand Up @@ -61,6 +61,42 @@ Takes the expression `x` and returns an equivalent expression with all macros re
"""
macroexpand(x::ANY) = ccall(:jl_macroexpand, Any, (Any,), x)

"""
@macroexpand

Return equivalent expression with all macros removed (expanded).
Copy link
Contributor

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.

Copy link
Contributor Author

@jw3126 jw3126 Sep 26, 2016

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?

Copy link
Contributor

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


There is a subtle difference between `@macroexpand` and `macroexpand` in that expansion takes place in
different contexts. This is best seen in the following example:

```jldoctest
julia> module M
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)
```
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).
Copy link
Contributor

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?

Copy link
Contributor Author

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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

Copy link
Member

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).

Copy link
Contributor Author

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.

Copy link
Contributor

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

Note that when calling `macroexpand` or `@macroexpand` directly from the REPL, both of these contexts coincide, hence there is no difference.
"""
macro macroexpand(code)
code_expanded = macroexpand(code)
QuoteNode(code_expanded)
end

## misc syntax ##

"""
Expand Down
30 changes: 30 additions & 0 deletions doc/stdlib/base.rst
Expand Up @@ -1517,6 +1517,36 @@ Internals

Takes the expression ``x`` and returns an equivalent expression with all macros removed (expanded).

.. function:: @macroexpand

.. Docstring generated from Julia source

Return equivalent expression with all macros removed (expanded).

There is a subtle difference between ``@macroexpand`` and ``macroexpand`` in that expansion takes place in different contexts. This is best seen in the following example:

.. doctest::

julia> module M
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)

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. Note that when calling macroexpand or @macroexpand directly from the REPL, both of these contexts coincide, hence there is no difference.

.. function:: expand(x)

.. Docstring generated from Julia source
Expand Down
1 change: 1 addition & 0 deletions doc/stdlib/io-network.rst
Expand Up @@ -1078,3 +1078,4 @@ Network I/O
.. Docstring generated from Julia source

The 32-bit byte-order-mark indicates the native byte order of the host machine. Little-endian machines will contain the value ``0x04030201``\ . Big-endian machines will contain the value ``0x01020304``\ .

2 changes: 1 addition & 1 deletion test/choosetests.jl
Expand Up @@ -33,7 +33,7 @@ function choosetests(choices = [])
"markdown", "base64", "serialize", "misc", "threads",
"enums", "cmdlineargs", "i18n", "workspace", "libdl", "int",
"checked", "intset", "floatfuncs", "compile", "parallel", "inline",
"boundscheck", "error", "ambiguous", "cartesian", "asmvariant"
"boundscheck", "error", "ambiguous", "cartesian", "asmvariant", "expr"
]

if Base.USE_GPL_LIBS
Expand Down
19 changes: 19 additions & 0 deletions test/replutil.jl
Expand Up @@ -447,3 +447,22 @@ let d = Dict(1 => 2, 3 => 45)
@test contains(replace(result, " ", ""), string(el))
end
end


# @macroexpand tests
macro seven_dollar(ex)
# simonbyrne example 18240
isa(ex,Expr) && ex.head == :$ ? 7 : ex
end

let
@test (@macroexpand @macroexpand x) == macroexpand(:(@macroexpand x))
@test (@macroexpand :(1+$y) ) == macroexpand(:( :(1+ $y)))
@test (@macroexpand @fastmath 1+2 ) == :(Base.FastMath.add_fast(1,2))
@test (@macroexpand @fastmath + ) == :(Base.FastMath.add_fast)
@test (@macroexpand @fastmath min(1) ) == :(Base.FastMath.min_fast(1))
@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 + $(Expr(:$, :x)))
end