Right, but I think that's a reasonable trade-off to make. If we tried for something declarative like
macro_rules, we're going to bump against its limitations and also want something like procedural macros (Rust's equivalent to what I'm proposing) in addition. I'd rather try have just one feature here, even if it means it's a little harder to get started, and bridge the gap with tutorials and tools like
swift package init --type macro.
That's interesting. The implicit package would likely have very different dependencies than where the source code is written, and in many cases be built for a different architecture, both of which can be problematic for inline code. For example, the macro code will need access to swift-syntax to parse/manipulate syntactic ASTs, whereas the main program will not. The macro code will build for the host platform (wherever you do your builds) whereas the main program will build for the target platform (wherever you run the code). If we're going to do inline macro code, I think we'll need to make the macro inputs untyped---i.e., you need a well-formed syntax tree, but it isn't type checked.
Yes, it does mirror the conformance. I wrote it this way because the
macro declaration is something that's visible to the main program, whereas the conformance is visible only to the package that's used by the compiler.
Yeah, this is a great idea. I went ahead and expanded the main
Macro protocol in the prototype to have all of the information that was in the
macro declaration: name, type signature, documentation. That way, the conformance becomes the single source of truth, and there's no declaration in the language.
There are downsides to this approach---where do you "jump to definition" on a macro? On the other hand, it eliminates a pile of repetition, and lets us defer defining macros in the surface language.
Yes, of course! I can't believe I forgot to write down the function use-case. I think we want such a macro to only affect the body of the function, and not the type signature. I'll add it to my next revision, thank you.
Picking up a whole bunch of use cases like this, without adding bespoke features for each, is exactly the goal of a macro system.
This is a good point. We already have attribute syntax for applying some additional semantics to a declaration; we should re-use that for applying macros to a declaration, rather than invent a new syntax like
#traced func f(). I do think the
# syntax makes sense for cases where we aren't annotating an existing thing, but generating a new thing, e.g., the expression cases like
That's a really interesting use case. The way in which you need to extend several other types makes it a tricky one, because it could start to feel like spooky-action-at-a-distance. It's worth thinking through this one!
Yeah, this is a missing piece. I should have talked about macro hygiene in general, and some kind of
gensym is probably the only effective strategy since we're dealing with parsing syntax.