Live data from Hacker News

Things Zig comptime won't do

matklad.github.io

121–130 of 252 posts

Re: Things Zig comptime won't do

#121
post #42

Earlier quoted context omitted.

Isn’t this kind of thing sort of the default thing in Lisp? Code is data so you can transform it.

Lisp is so powerful, but without static types you can't even do basic stuff like overloading, and have to invent a way to even check the type(for custom types) so you can branch on type.

> but without static types

So add static types.

https://github.com/coalton-lang/coalton

Re: Things Zig comptime won't do

#122
post #82

Earlier quoted context omitted.

> Rust’s macro system likewise is also very weak. How so? Rust procedural macros operate on token stream level while being able to tap into the parser, so I struggle to think of what they can't do, aside from limitations on the syntax of the macro.

Rust macros don't really understand the types involved. If you have a derive macro for #[derive(MyTrait)] struct Foo { bar: Bar, baz: Baz, } then your macro can see that it references Bar and Baz, but it can't know anything about how those types are defined. Usually, the way to get around it is to define some trait on both Bar and Baz, which your Foo struct depends on, but that still only gives you access to that inf…

Thanks, that’s exactly what I was referencing. In lisp the type doesn’t matter as much, just the structure, as maps or other dynamic pieces will be used. However in typed languages it matters a lot.

Re: Things Zig comptime won't do

#123
post #55

zig's comptime has some (objectively: debatable? subjectively: definite) shortcomings that the zig community then overcomes with zig build to generate code-as-strings to be lateron @imported and compiled. Practically, "zig build"-time-eval. As such there's another 'comptime' stage with more freedom, unlimited run-time (no @setEvalBranchQuota), can do IO (DB schema, network lookups, etc.) but you lose the freedom to g…

> The zig community bewilders me at times with their love for lashing themselves. The sort of discussions which new sort of self-harm they'd love to enforce on everybody is borderline disturbing. Personally, I find the idea that a compiler might be able to reach outside itself completely terrifying (Access the network or a database? Are you nuts?). That should be 100% the job of a build system. Now, you can certainly…

> Personally, I find the idea that a compiler might be able to reach outside itself completely terrifying (Access the network or a database? Are you nuts?).

Why though? F# has this feature called TypeProviders where you can emit types to the compiler. For example, you can do do:

   type DbSchema = PostgresTypeProvider
   type WikipediaArticle = WikipediaTypeProvider

and now you have a type that references that Article or that DB. You can treat it as if you had manually written all those types. You can fully inspect it in the IDE, debugger or logger. It's a full type that's autogenerated in a temp directory.

When I first saw it, I thought it was really strange. Then thought about it abit, played with it, and thought it was brilliant. Literally one of the smartest ideas ever. It's first class codegen framework. There were some limitations, but still.

After using it in a real project, you figure out why it didn't catch on. It's so close, but it's missing something. Just one thing is out of place there. The interaction is painful for anything that's not a file source, like CsvTypeProvider or a public internet url. It does also create this odd dependenciey that your code has that can't be source controlled or reproduced. There were hacks and workarounds, but nothing felt right for me.

It was however, the best attempt at a statically typed language trying to imitate python or javascript scripting syntax. Where you just say put a db uri, and you start assuming types.

Re: Things Zig comptime won't do

#124

Earlier quoted context omitted.

It's not novel. D pioneered compile time function execution (CTFE) back around 2007. The idea has since been adopted in many other languages, like C++. One thing it is used for is generating string literals, which then can be fed to the compiler. This takes the place of macros. CTFE is one of D's most popular and loved features.

If I understand TFA correctly, the author claims that D’s approach is actually different: https://matklad.github.io/2025/04/19/things-zig-comptime-won... “In contrast, there’s absolutely no facility for dynamic source code generation in Zig. You just can’t do that, the feature isn’t! [sic] Zig has a completely different feature, partial evaluation/specialization, which, none the less, is enough to cover most of use-c…

The partial evaluation/specialization is accomplished in D using a template. The example from the link:

    fn f(comptime x: u32, y: u32) u32 {
        if (x == 0) return y + 1;
        if (x == 1) return y * 2;
        return y;
    }
and in D:

    uint f(uint x)(uint y) {
        if (x == 0) return y + 1;
        if (x == 1) return y * 2;
        return y;
    }
The two parameter lists make it a function template, the first set of parameters are the template parameters, which are compile time. The second set are the runtime parameters. The compile time parameters can also be types, and aliased symbols.

Re: Things Zig comptime won't do

#125

Earlier quoted context omitted.

And those index values are just pointers by another name!

It's not "just pointers", because they can have additional semantics and assurances beyond "give me the bits at this address". The index value can be tied to a specific container (using new types for indexing so tha you can't make the mistake of getting value 1 from container A when it represents an index from container B), can prevent use after free (by embedding data about the value's "generation" in the key), and…

Yes, but like raw pointers, they lack lifetime guarantees and invite use after free vulnerabilities

Re: Things Zig comptime won't do

#126

Earlier quoted context omitted.

Oh, that does sound tough in rust! I'm not even sure how to approach it; good to know it's a useful pattern in other langs.

Well, one can always write unsafe Rust. Although the more usual pattern here is to ditch pointers and instead have a giant array of objects referring to each other via indices into said array. But this is effectively working around the borrow checker - those indices are semantically unchecked references, and although out-of-bounds checks will prevent memory corruption, it is possible to store index to some object onl…

>unsafe rust Which is worse than C

Re: Things Zig comptime won't do

#127
post #85

Earlier quoted context omitted.

I was quite impressed with Austral[0], which used Linear Types and avoids the whole Rust-like implementation in favour of a more easily understandable system, albeit slightly more verbose. [0] https://borretti.me/article/introducing-austral

Austra's concept are interesting but the introduction doesn't show how to handle correctly errors in this language..

Austral's specification is one of the most beautiful and well-written pieces of documentation I have ever found. It's section on error handling in Austral[0] cover everything from rationale and alternatives to concrete examples of how exceptions should be handled in conjunction with linear types.

https://austral-lang.org/spec/spec.html#rationale-errors

Re: Things Zig comptime won't do

#128
post #82

Earlier quoted context omitted.

Definitely, you can do most of those things in Nim without macros using templates and compile time stuff. It’s preferable to macros when possible. Julia has fantastic compile time abilities as well. It’s beautiful to implement an incredibly fast serde in like 10 lines without requiring other devs to annotate their packages. I wouldn’t include Rust on that list if we’re speaking of compile time and compile time type a…

> Rust’s macro system likewise is also very weak. How so? Rust procedural macros operate on token stream level while being able to tap into the parser, so I struggle to think of what they can't do, aside from limitations on the syntax of the macro.

It doesn't have access to the type system, for example. It just sees it's input as what you typed in the code. It wouldn't be able to see through aliases.

Re: Things Zig comptime won't do

#129
post #18

Yes! To me, the uniqueness of Zig's comptime is a combination of two things: 1. comtpime replaces many other features that would be specialised in other languages with or without rich compile-time (or runtime) metaprogramming, and 2. comptime is referentially transparent [1], that makes it strictly "weaker" than AST macros, but simpler to understand; what's surprising is just how capable you can be with a comptime me…

[deleted]

Re: Things Zig comptime won't do

#130
post #104
post #64

Earlier quoted context omitted.

They are not advocating for IO in the compiler, but everything else that other languages can do with macros: run commands comptime, generate code, read code, modify code. It's proven to be very useful.

I'm going to make you defend that statement that they are "useful". I would counter than macros are "powerful". However, "macros" are a disaster to debug in every language that they appear. "comptime" sidesteps that because you can generally force it to run at runtime where your normal debugging mechanisms work just fine (returning a type being an exception). "Macros" generally impose extremely large cognitive overhe…

> However, "macros" are a disaster to debug in every language that they appear.

I have only used proper macros in Common Lisp, but at least there they are developed and debugged just like any other function. You call `macroexpand` in the repl to see the output of the macro and if there's an error you automatically get thrown in the same debugger that you use to debug other functions.

Post reply on HN