top | item 42620503

(no title)

ScottRedig | 1 year ago

Hi, article author here. I was motivated to write this post after having trouble articulating some of its points while at a meetup, so that's why the goal of this post was focused on explaining things, and not being critical.

So at least address your points here:

* I do agree this is a direct trade-off with Zig style comptime, versus more statically defined function signatures. I don't think this affects all code, only code which does such reasoning with types, so it's a trade-off between reasoning and expressivity that you can make depending on your needs. On the other hand, per the post's view 0, I have found that just going in and reading the source code easily answers the questions I have when the type signature doesn't. I don't think I've ever been confused about how to use something for more than the time it takes to read a few dozen lines of code.

* Your specific example for recursive generic types poses a problem because a name being used in the declaration causes a "dependency loop detected" error. There are ways around this. The generics example in the post for example references itself. If you had a concrete example showing a case where this does something, I could perhaps show you the zig code that does it.

* Type checking happens during comptime. Eg, this code:

  pub fn main() void {
      @compileLog("Hi");
      const a: u32 = "42";
      _ = a;
      @compileLog("Bye");
  }
Gives this error:

  when_typecheck.zig:3:17: error: expected type 'u32', found '*const [2:0]u8'
   const a: u32 = "42";
                  ^~~~
  Compile Log Output:
  @as(*const [2:0]u8, "Hi")
So the first @compileLog statement was run by comptime, but then the type check error stopped it from continuing to the second @compileLog statement. If you dig into the Zig issues, there are some subtle ways the type checking between comptime and runtime can cause problems. However it takes some pretty esoteric code to hit them, and they're easily resolved. Also, they're well known by the core team and I expect them to be addressed before 1.0.

* I'm not sure what you mean by hygiene, can you elaborate?

discuss

order

mananaysiempre|1 year 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.)

throwawaymaths|1 year ago

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.

Validark|1 year ago

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.