-
Notifications
You must be signed in to change notification settings - Fork 7
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
Cannot register macro with same name #43
Comments
I just realized #2 wouldn't support docstrings, oops! @stable begin
""" foo() """
foo() = 1
end |
For 1, would this mean having an if statement over all macros — at every occurrence of a macro — which only gets evaluated at runtime? |
Maybe another option is to allow a per-module |
Unfortunately it might, but I'm not sure and am hoping to get it to compile away. I'm still working on a prototype, which is admittedly difficult. Returning an expression like I mentioned doesn't work. My next idea is to use
So the preferences file would additionally store the module and the macro would pass in |
Assuming the modules can be recognized and we store the macro in EDIT: The methods would be generated from the table, whereas the dispatch might be able to occur at compile-time since macros can be returned as values from the expressions julia> macro f() Symbol("@doc") end;
julia> typeof(@f)
Core.var"#@doc" |
By the way, I’m not sure Preferences.jl makes sense here, because Preferences is more for compiler flag-like stuff. But declaring incompatible macros seems like it should be a constant and thus live in the code. |
@ven-k how is |
Am I doing this incorrectly? Maybe it could be done by having the macro check whether the table already exists in the current module and define it if not? julia> module A
using DynamicQuantities
@register_unit MyVolt 1u"V"
end;
julia> module B
using DynamicQuantities
@register_unit MyVolt 2u"V"
end;
ERROR: LoadError: Unit `MyVolt` is already defined as `1.0 m² kg s⁻³ A⁻¹` I'm working on #1; still trying to fix tests but it might be viable. julia> Base.remove_linenums!(@macroexpand @stable @show 1)
┌ Warning: `@stable` found no compatible functions to stabilize
│ source_info = :(#= REPL[30]:1 =#)
│ calling_module = Main
└ @ DispatchDoctor._Stabilization ~/Documents/programming/forks/DispatchDoctor.jl/src/stabilization.jl:40
quote
var"##435"(::DispatchDoctor._Interactions.IncompatibleMacro) = begin
begin
Base.println("1 = ", Base.repr(begin
local var"#196#value" = 1
end))
var"#196#value"
end
end
var"##435"(::DispatchDoctor._Interactions.CompatibleMacro) = begin
begin
Base.println("1 = ", Base.repr(begin
local var"#197#value" = 1
end))
var"#197#value"
end
end
var"##435"(::DispatchDoctor._Interactions.DontPropagateMacro) = begin
1
end
var"##435"(DispatchDoctor._Interactions.CompatibleMacro())
end |
Option #1 only works for the return value of expressions, which is a pretty big limitation. But it was fun to try it out at least |
Simple way to get this working: see
Maybe what we need is a 2-arg method with both the module and the macro symbol, so a user’s registered macro would only have an effect if used within that same calling module. |
I think the best API would be: @register_macro DontPropagateMacro @doc Because We want the Then, const MACRO_BEHAVIOR = (;
table=Dict([
Symbol("@stable") => [(Main, IncompatibleMacro)],
Symbol("@unstable") => [(Main, IncompatibleMacro)],
Symbol("@doc") => [(Main, DontPropagateMacro)],
Symbol("@assume_effects") => [(Main, IncompatibleMacro)],
]),
lock=Threads.SpinLock()
) Basically, the program will look up a macro symbol, and loop through the list of behaviors, and find the most specific behavior for the current module (where So, if you have module A
module mymacro(arg)
return esc(arg)
end
@register_macro DontPropagateMacro @mymacro
end Then it will create (or push to) the symbol in the macro table: behaviors = get!(MACRO_BEHAVIOR.table, Symbol("@mymacro"), Tuple{Module,MacroBehavior}[])
# ^ This will automatically create the array if it does not exist
push!(behaviors, (Main.A, DontPropagateMacro)) And so whenever However, I do feel like this is a bit complicated, and it would be better to take @ven-k's approach. I still don't quite understand how it works though and if it's possible to use it here. |
This fixes the problems I had trying to compare macros by identity! It is a bit more limited such that an external package cannot register defaults for arbitrary modules though. |
Ah, my non-org notifications are sent to an inactive mail; almost missed this thread. So in an external package like ExternalUnitRegistration.jl the imported collections like We can still use that MyWb as a variable, outside the scope of ExternalUnitReg, because a new variable is explicitly declared in the "ExternalUnitRegistration" module here. As it is of the same |
The current design limits the extensibility of the package, and I argue that no macro should be given special treatment. While I'm not sure if this is an issue when a user depends on a package registering the same name, the predefined behavior intended for
Distributed.@everywhere
,Turing.@model
, andMacroTools.@capture
prevents anyone else from registering their own macro under one of those names. This applies to theBase
andCore
macros too, but one could argue that users shouldn't be shadowing those names anyways. While admittedly an edge case, this issue could increase in severity as DispatchDoctor.jl becomes more widely used or even moved toCore
#40.Possible solutions
I am personally in favor of #1
or #2, maybe #4, but not #3.1. Compare macro equality at run-time (EDIT: or compile-time?)
I think that this can be implemented by storing both the module and name of each macro in
MACRO_BEHAVIOR
and returning an expression fromget_macro_behavior
and_stabilize_all
. The expression would contain an if-statement for the union of behaviors of a given name inMACRO_BEHAVIOR
and the default behavior.EDIT: This might be able to be done at compile-time (at least, in the same way that LLVM can optimize it away), since macros have unique types.
Pros:
@unstable
or@stable default_mode = "disable"
Cons:
IncompatibleMacro
case does not generate a new expression anyways2. Treat all macros as incompatible
Pros:
@stable
within macrosCons:
3. Make macro behavior opt-in only
The
MACRO_BEHAVIOR
dictionary would be empty by default, requiring users to register macros as needed.Pros:
Cons:
MACRO_BEHAVIOR
as their dependencies (and I don't know if this is the case), then this can still result in non-extensibility4. Make macro behavior toggle-able or customizable with preferences
The text was updated successfully, but these errors were encountered: