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

Macroexpand1 #21662

Merged
merged 6 commits into from
Jul 13, 2017
Merged

Macroexpand1 #21662

merged 6 commits into from
Jul 13, 2017

Conversation

jw3126
Copy link
Contributor

@jw3126 jw3126 commented May 1, 2017

This PR adds a non recursive variant of macroexpand, see #19365. What is the preferred API?

  • macroexpand1(ex), @macroexpand1(ex) ?
  • macroexpand(ex, recursive::Bool), @macroexpand(ex, recursive) ?

Both? Something else? For REPL usage I think @macroexpand1 is the most convenient, albeit a bit cryptic.

test/replutil.jl Outdated
@test macroexpand1(macroexpand1(ex)) == macroexpand(ex)
ex = :(@nest2b 42)
@test macroexpand1(ex) != macroexpand(ex)
@test macroexpand1(macroexpand1(ex)) == macroexpand(ex)
Copy link
Member

@JeffBezanson JeffBezanson May 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be 4 spaces.

@@ -411,8 +412,10 @@
(m (cdr form)))
;; m is the macro's def module
(rename-symbolic-labels
(julia-expand-macros
((lambda (ex) (julia-expand-macros-limited ex (- max-depth 1)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lambda is not necessary.

(define (julia-expand-macros e)
(cond ((not (pair? e)) e)
(define (julia-expand-macros-limited e max-depth)
(cond ((eq? max-depth 0) e)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use =.

@JeffBezanson
Copy link
Member

Since we have both macro and function versions of macroexpand, if we add this we should probably have both here too.

@StefanKarpinski
Copy link
Member

How about expressing this with a recur::Bool=true keyword instead?

@jw3126
Copy link
Contributor Author

jw3126 commented May 1, 2017

@StefanKarpinski I though about this. Since recur is consistently spelled recursive in Base the choice is between @macroexpand1 ex and @macroexpand recursive=false ex.
But the latter is quite a mouthful, for quick REPL debugging. So I decided for the former.

@alyst
Copy link
Contributor

alyst commented May 1, 2017

One can also use depth::Bool = -1 (-1 for unlimited, 0 for no-op), depth=1 is shorter.

@jw3126 jw3126 closed this May 1, 2017
@jw3126 jw3126 reopened this May 1, 2017
@jw3126
Copy link
Contributor Author

jw3126 commented May 1, 2017

In principle one could have macroexpand(ex;depth::Int=-1) with depth=-1,2,3... all allowed. In femptolisp it is implemented like that. However I was not sure how to pass the depth parameter from julia to flisp.

@vtjnash
Copy link
Member

vtjnash commented May 1, 2017

Could we make macroexpand mean the "expand one macro" operations, and add a keyword all (fully?) to do the current behavior?

@jw3126
Copy link
Contributor Author

jw3126 commented May 1, 2017

@vtjnash I like this idea, I guess the cases where you actually want to expand everything are library code, where typing some keyword does not hurt much. Of course this change is breaking in a subtle way.

@jw3126 jw3126 closed this May 5, 2017
@jw3126 jw3126 reopened this May 5, 2017
@jw3126
Copy link
Contributor Author

jw3126 commented May 5, 2017

So do you guys prefer @vtjnash idea of having only
macroexpand(code; recursive=false)
instead of
macroexpand, macroexpand1 ?
If so, how should the macroversion look like? Should it also take a keyword argument?

@jw3126
Copy link
Contributor Author

jw3126 commented May 5, 2017

I think the usage pattern is as follows:

  • For REPL use, you want the macro and you want it non recursive.
  • For library use, you want the function and you want it recursive.

@TotalVerb
Copy link
Contributor

What is the difference between this function and simply calling the macro itself (e.g. with getfield(current_module(), Symbol("@foo"))(...)?

@vtjnash
Copy link
Member

vtjnash commented May 6, 2017

In this case, both exist because a macro is a convenient way to quote an expression. In other cases, they differ in the post-processing done by a macro to apply hygiene. In fact, I was working on making an example of this today! The following diff should have no functional change (other than the catch_backtrace on error, given that I know @doc is docm): vtjnash@81a202c Enjoy! 😃

@jw3126
Copy link
Contributor Author

jw3126 commented May 6, 2017

@TotalVerb I was not aware, that you could get a handle on a macro like that. Here is a concrete example of hygiene application:

julia> getfield(current_module(), Symbol("@show"))(:x)
quote 
    println("x = ", repr(begin  # show.jl, line 243:
                value = $(Expr(:escape, :x))
            end))
    value
end

julia> macroexpand1(:(@show x))
quote 
    (Base.println)("x = ", (Base.repr)(begin  # show.jl, line 243:
                #6#value = x
            end))
    #6#value
end

@jw3126
Copy link
Contributor Author

jw3126 commented Jun 28, 2017

Sorry, something went terribly wrong during rebase.

@jw3126 jw3126 force-pushed the macroexpand1 branch 3 times, most recently from c65d9fb to 7d99913 Compare June 30, 2017 05:15
base/expr.jl Outdated

* While [`macroexpand`](@ref) has an explicit `module` argument, `@macroexpand` always expands with respect to the module in which it is called. This is best seen in the following example:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrap long lines

@jw3126
Copy link
Contributor Author

jw3126 commented Jul 1, 2017

What are the CI fails about?

@@ -275,6 +275,8 @@ Base.gc
Base.gc_enable
Base.macroexpand
Base.@macroexpand
Base.macroexpand1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the documentation fails to build because this doesn't exist any more

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks very much!

@jw3126
Copy link
Contributor Author

jw3126 commented Jul 2, 2017

I think this PR is ready for merge.

@KristofferC
Copy link
Member

Bump on review. @vtjnash?

Copy link
Member

@vtjnash vtjnash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some minor comments. This looks good – thanks for implementing it!

(julia-expand-macros
(resolve-expansion-vars form m))))))
(julia-expand-macros-limited (resolve-expansion-vars form m) (- max-depth 1))
))))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

closing parens should be all on the same (previous) line

((eq? (car e) 'module) e)
(else
(map julia-expand-macros e))))
(map (lambda (ex) (julia-expand-macros-limited ex max-depth) ) e))))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no space before closing parens

@@ -466,11 +466,12 @@

;; macro expander entry point

(define (julia-expand-macros e)
(cond ((not (pair? e)) e)
(define (julia-expand-macros-limited e max-depth)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be more straightforward to define this as a boolean with a default value:
(define (julia-expand-macros e (recursive #t))
I'm not sure there's going to be a use case for arbitrary depths, so it's nice just to keep the implementation simple.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find the max depth implementation actually slightly simpler.

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 I will try a default value (define (julia-expand-macros e (max-depth -1)). Did not expect this feature.

base/expr.jl Outdated

Takes the expression `x` and returns an equivalent expression with all macros removed (expanded)
for executing in module `m`.
The `recursive` keyword controls whether deeper levels of nested macros are also expanded.
This is demonstrated in the example below:
```julia
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be jldoctest, but would be good for someone else to confirm

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It contains stuff like

:(#= REPL[16]:6 =# M.@m1)

that probably makes the doctest very fragile?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be julia-repl if it uses repl format but is not a doctest.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks was not aware of julia-repl.

@jw3126
Copy link
Contributor Author

jw3126 commented Jul 13, 2017

@vtjnash LGTY?

@vtjnash vtjnash merged commit a7f24c4 into JuliaLang:master Jul 13, 2017
@tkelman
Copy link
Contributor

tkelman commented Jul 14, 2017

does this really need to be exported?

@vtjnash
Copy link
Member

vtjnash commented Jul 14, 2017

We could move them to Base.Meta or interactiveutils.jl, but it would be really annoying for these not to be easily available at the REPL.

@tkelman
Copy link
Contributor

tkelman commented Jul 14, 2017

how often is the non recursive version needed?

@KristofferC
Copy link
Member

More often than the recursive?

jeffwong pushed a commit to jeffwong/julia that referenced this pull request Jul 24, 2017
`@macroexpand1` is a non recursive variant of `@macroexpand`

The `macroexpand` function now also takes an keyword argument `recursive` to indicate whether to expand one level of macros or all macros recursively.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants