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

Allow macros to apply to modules #48501

Closed
non-Jedi opened this issue Feb 2, 2023 · 3 comments
Closed

Allow macros to apply to modules #48501

non-Jedi opened this issue Feb 2, 2023 · 3 comments

Comments

@non-Jedi
Copy link
Contributor

non-Jedi commented Feb 2, 2023

I didn't see an issue for this, but there very well might be one already.

Occasionally I find myself wanting to transform the language consistently enough that I end up writing:

module MyModule
@mymacro begin
    ...
end
end

This works okay but seems clunky compared to being able to simply write "@mymacro MyModule ..." which currently errors with "ERROR: syntax: "module" expression not at top level". Additionally, editors try to insert an extra level of indentation from the begin block which is undesired.

Some practical examples off the top of my head where applying a macro in this way might be natural:

  1. The packages implementing various versions of underscore-based partial application (RFC: curry underscore arguments to create anonymous functions #24990 and related) could have their macros applied to entire modules to give a better demonstration of what the feature would look like as a language feature.
  2. Apply @inbounds to an entire module (discussed somewhat in Consider removing --check-bounds=no? #48245).
  3. A user may wish to use Floops.jl for all loops in a module.
  4. You could write a macro to apply to a module which would automatically add emacs-style hooks as extension points to all functions defined in the module.

Downsides to this proposal from the Julia user's point of view (I make no claims as to the implementation difficulty of this idea):

  • Within a package, you have to define a submodule anyway to make use of this syntax with non-Base macros since imports happen within the package's module.
  • Decreases readability by changing the meaning of code non-locally (but this can happen anyway by simply wrapping everything in a @mymacro begin block).
  • Potential to bring something like the "Lisp curse" to Julia by making it easy to write in your own special Julia dialect.
@quinnj
Copy link
Member

quinnj commented Feb 2, 2023

You just need to return your module expression wrapped in a :toplevel Expr, like:

julia> macro foo(mod)
           _, name, code = mod.args
           pushfirst!(code.args, :(const x = 1))
           return esc(Expr(:toplevel, mod))
       end
@foo (macro with 1 method)

julia> @foo module Foo
       const y = 2
       end
Main.Foo

julia> Foo.x
1

@quinnj quinnj closed this as completed Feb 2, 2023
@non-Jedi
Copy link
Contributor Author

non-Jedi commented Feb 4, 2023

Ah! Thanks. :)

@uniment
Copy link

uniment commented Feb 4, 2023

You probably know this already, but I thought I’d highlight:

When wrapping lots of code in a macro, that macro executes before any nested macro calls. So, for example, my underscore demo would break Chain.jl, whereas if it was a language feature it would execute after macro expansion so it wouldn’t break anything.

Many users of Chain.jl put this phenomenon to use when using DataFramesMeta.jl, because @chain will thread the result of the previous expression into the first argument of the next call, even if it’s a macro call such as @rsubset. A language feature couldn’t be expected to do this, so this is a notable point of difference.

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

No branches or pull requests

3 participants