Hacker News new | ask | show | jobs
by mananaysiempre 527 days ago
“Hygiene” in the context of macro systems refers to the user’s code and the macro’s inserted code being unable to capture each other’s variables (either at all or without explicit action on part of the macro author). If, say, you’re writing a macro and your generated code declares a variable called ‘x’ for its own purposes, you most probably don’t want that variable to interfere with a chunk of user’s code you received that uses an ‘x’ from an enclosing scope, even if naïvely the user’s ‘x’ is shadowed by the macro’s ‘x’ at the insertion point of the chunk.

It’s possible but tedious and error-prone to avoid this problem by hand by generating unique identifier names for all macro-defined runtime variables (this usually goes by the Lisp name GENSYM). But what you actually want, arguably, is an extended notion of lexical scope where it also applies to the macro’s text and macro user’s program as written instead of the macroexpanded output, so the macro’s and user’s variables can’t interfere with each other simply because they appear in completely different places of the program—again, as written, not as macroexpanded. That’s possible to implement, and many Scheme implementations do it for example, but it’s tricky. And it becomes less clear-cut what this even means when the macro is allowed to peer into the user’s code and change pieces inside.

(Sorry for the lack of examples; I don’t know enough to write one in Zig, and I’m not sure giving one in Scheme would be helpful.)

2 comments

zig comptime is not a macro system and you can't really generate code in a way that makes hygeine a thing to worry about (there is no ast manipulation, you can't "create variables"). the only sort of codegen you can do is via explicit conditionals (switch, if) or loops conditioned on compile time accessible values.

thats still powerful, you could probably build a compile time ABNF parser, for example.

Zig is also not the only language that has interesting compile-time features. V (Vlang)[1] and D (Dlang) (among others), has them too. It's kind of weird that this is promoted so much with Zig, as if other languages don't have a lot of this.

[1]: https://github.com/vlang/v/blob/master/doc/docs.md#compile-t...

Surely there's a way to generate code by manipulating an AST structure? Is there some reason this can't be done in Zig or is it just that no one has bothered?

Doing it this way is more verbose but sidesteps all hygiene issues.

Zig lets you inspect type info (including, eg, function signatures), but it doesn't give you raw access to the AST. There's no way to access the ast of the body of the function. As highlighted by view 0 in my article, I consider this a good thing. Zig code can be read without consideration for which pieces are comptime or not, something that heavy AST manipulation loses.

Though, if you really wanted to do stupid things, you could use @embedFile to load a Zig source file, then use the Zig compiler's tokenizer/ast parser (which are in the standard library) to parse that file into an AST. Don't do that, but you could.

This reminds me of comptime brainfuck interpreter https://github.com/edqx/ccb
Generating code during comptime is explicitly forbidden by the author. You can still generate code during build.zig of course.
Zig disallows ALL shadowing (basically variable name collisions where in the absence of the second variable declaration the first declaration would be reachable by the same identifier name).

Generating a text file via a writer with the intent to compile it as source code is no worse in Zig than it is in any other language out there. If that's what you want to do with your life, go ahead.