I propose to deprecate macro
both as a construct and as a concept.
I came to this idea from the fact that whenever I write a macro that macro often does nothing more than call a function which does the actual code transformation. That way it's much easier to debug, and if I need to use the transformation in a non-macro setting I can easily do so.
My suggestion is simple:
Expr
s@somefunction(someexpr)
syntax will transform that someexpr
in exactly the way it does now.The conceptual change is going away from the idea of a macro as an entity, and moving all the work it does into the special semantics of the calling of that function (to do the code substitution).
I would imagine that this isn't much of a change in implementation to what is going on now, but I think it brings some conceptual clarity and some practical benefits:
My guess is that we got to the current situation by a quirk. We adopted define-macro
from scheme but did not adopt scheme's behaviour of having no special syntax for applying macros. Put differently, scheme could not get rid of define-macro
because it has no syntax to make the differentiation that macro substitution happens at a different stage of compilation/interpretation.
I'm of course open to the possibility that there is some glaring problem with this proposal that I haven't seen. On slack @oxinabox noted that one problem could be that in current code there is a lot of overlap between function names and macro names, This is true, but I think a relatively small problem. Besides, we have multiple dispatch we could in most cases just specialise.
Deprecating macros would be a breaking change of course. One non-breaking change would be the ability to call code transforming functions as using some variant of the @
syntax
That way it's much easier to debug, and if I need to use the transformation in a non-macro setting I can easily do so.
You can call a macro directly and that's easy with var
string macro
As I said above, we would mean we wouldn't have arbitrary decisions about what goes in a macro and what goes in a function
There's nothing arbitrary here. Nothing more arbitrary then when you want to call one function from another function.
It's easier to understand. We don't need to teach newcomers that macros are just functions that transform expressions, because they would obviously be functions that just transform expressions
This is really the easiest aspect of a macro and macro really aren't "just functions that transform expressions". There are a LOT more to macros than this, like handling the scope and such. In fact, I can't think of a single case where you want something to be both a macro and a normal function. The fact that macros are almost impossible to nest correctly without breaking hygene right now is a regression and a separate problem that needs to be fixed. When you define a macro, it is how people should use it and it shouldn't be any difference whether it's used by a user or someone that wants to wrap your macro. Calling the macro body directly without going through the macro expander first completely disgards all scope information and whenever you are doing it for macros someone else defined it is almost always wrong.
This makes sense in that macros are already just functions (whose names begin with @
). One obstacle is that we add extra arguments __source__
and __module__
to macros; I suppose you'd have to add them manually if we defined macros with the function
keyword. I also think it can be useful to have both macro and function versions of the same name, e.g. @printf
requiring a static format string vs. printf
where everything is at run time. That seems more useful to me than having printf
return the macro expansion of @printf
, which is mostly useful for debugging and can already be done in other ways. So I think I'm mostly opposed to this.
This makes sense in that macros are already just functions (whose names begin with
@
)
I think it actually makes even more sense than only that, since
julia> x = 3; @Base.show x;
x = 3
Most helpful comment
This makes sense in that macros are already just functions (whose names begin with
@
). One obstacle is that we add extra arguments__source__
and__module__
to macros; I suppose you'd have to add them manually if we defined macros with thefunction
keyword. I also think it can be useful to have both macro and function versions of the same name, e.g.@printf
requiring a static format string vs.printf
where everything is at run time. That seems more useful to me than havingprintf
return the macro expansion of@printf
, which is mostly useful for debugging and can already be done in other ways. So I think I'm mostly opposed to this.