Motivated by Seeking feedback on Chainables before registering it, I initially thought that that proposed new package was modifying the behavior of existing macros (like @chain from Chain.jl). I think that was a misunderstanding on my part, but it does raise the general question of if and how it is possible for a package to safely extend the functionality of macros defined by another package.
The potential concern here is “type piracy”, which is defining methods for functions you don’t own for arguments where you don’t own the type of at least one of the arguments. Type piracy is generally considered “bad” (and something that I would at least flag as a concern for packages being registered in the General registry) because they make reasoning about Julia code even harder than it already is with the inherent ambiguity of multiple dispatch: some package deep down in the dependency graph could significantly change the behavior of a program in very unexpected and buggy ways. Type piracy also leads to code invalidation, where (cached) pre-compiled code is not longer valid, causing a lot of the “time-to-first-X” problems that the Julia ecosystem has long been plagued with.
Macros fundamentally have similar multiple-dispatch capabilities as functions. However, as a related StackOverflow post points out:
You can add new methods, but the types you can dispatch on are limited:
Expr,Symbol, literal numbers
All of these are Julia builtins, so from a type piracy perspective, no package could possibly “own” these. By the definition of type piracy, that would mean that you can only define methods for @mymacro if you own @mymacro, and turn any extension of an existing macro in a different package into “type piracy”.
Is that an accurate reading? Should macros be limited to the packages that define them?
It is certainly possible that some of the issues with type piracy that apply to function methods do not apply to macro methods: I’m not sure if the invalidation of compiled code is an issue, since macros are expanded at compile time. That hints at a potential new issue though: what if a package gets precompiled with some macro definition, and then later on loading another package changes the definition of the macro. Does Julia track this and recompile the original code? Does it ignore the redefinition, since the macro was already expanded? Is the behavior defined?
It would be interesting if someone with in-depth knowledge on macros and Julia’s internals could shed some light on the technical details here. And, more broadly: what should be the “best practices” around extending, or avoiding to extend, macros?