Syntactic macros are not hygienic, meaning that the way in which a macro expansion is processed depends on the environment in which it is expanded, and can affect that environment.
Given Swift’s emphasis on safety, this strikes me as a strange choice. The community will grow a collection of best practices to avoid non-hygienic foot guns, but convention over compiler-guaranteed safety doesn’t seem very Swifty.
We propose to provide a builtin macro that names the module and the ExpressionMacro type name within the macro declaration following an =, e.g.,
@freestanding(expression)
macro stringify<T>(_: T) -> (T, String) =
#externalMacro(module: "ExampleMacros", type: "StringifyMacro")
Given that we need to introduce macro to the language to support this, it seems strange to me that the right-hand side of this declaration is a stringly-typed macro.
Presumably this macro declaration appears in the module vending the macro, alongside the StringifyMacro type. If so, I’d expect the right-hand side to just be StringifyMacro.self. (In fact, the #colorLiteral example at the end of the proposal uses nearly this syntax.)
I suspect the choice of #externalMacro is motivated by the chained expansion of macros discussed in the detailed design. I’m afraid the choice strikes me as more clever than user friendly.
Also apropos to where the macro declaration appears, is the assumption that all macro declarations are public by default? It seems they must be in order to be useful, since macro declarations can’t be co-located with the code that uses them. Or is it just that StringifyMacro must be declared in a different module?
(I recognize that the module/plug-in structure is to appear in another proposal, but #externalMacro takes a module name and so doesn’t seem reviewable without some sense of what code is expected to live where.)
macro-declaration -> macro-head identifier generic-parameter-clause[opt] macro-signature macro-definition[opt] generic-where-clause[opt]
What does a macro-declaration that omits the macro-definition even mean? How could such a macro be expanded? Is such a macro-declaration a built-in macro? If so, why is macro-definition optional in the surface syntax? Users of the compiler couldn’t define such a thing, could they?
The section “Macros in the Standard Library” proposes lifting many compiler built-ins into the standard library as (almost) macros. Is the idea that the compiler will expose underscored methods that the standard library uses to implement these macros?
What types are proposed to define these macros? Are the macro definitions public but the Macro conforming types internal? The proposed language features don’t seem to support that.
I don’t know how to review the proposed standard library additions when the proposed language features aren’t adequate to define them.
- What is your evaluation of the proposal?
I love the power offered. I’m concerned with how ad-hoc the approach seems. I think too many details regarding standard library macros are left unstated for a fair evaluation.
- Is the problem being addressed significant enough to warrant a change to Swift?
Definitely.
- Does this proposal fit well with the feel and direction of Swift?
The fact that macro inputs and outputs are type-checked is appropriate to Swift. The fact that macros are non-hygienic seems like a purely pragmatic decision that we’ll regret.
- If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
I’ve used macros in Racket/Scheme and in C. The proposal compares favorably to C macros.
- How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
I studied the revised proposal and read the initial pitch and discussion thread, but missed the intervening bits.
(Apologies for coming late to this process. Health and family issues have had me sidelined.)