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.)