4 ms·
I am not good enough of a programmer to understand all the meta/compile time programming stuff. So thank you for this actual concrete example of it being used.
by wisnoskij 4y ago
I am not good enough of a programmer to understand all the meta/compile time programming stuff.
So thank you for this actual concrete example of it being used. But why is that useful? Why regenerate a database every time you compile? Now that you have compiled the program once, can you not just delete that code and reduce your compile time to a fraction of what it was?
- skybrian 4y agoIt's useful due to convenient notation. Otherwise, you'd need to write a separate program that generates the code and call it using a separate Makefile target. (And in that case, you might check in the generated code; this is commonly done for Go packages, so that downstream dependencies don't need to run the code generator tools.) Adding a compile-time parameter to a function is considerably more convenient than writing a code generator. Zig's compile-time performance might be a problem someday, but there is likely not enough Zig code yet for this to matter. Perhaps they'll add caching if it ever turns out to be an issue.
- anonymoushn 4y agoI think it's supposed to be cached in the same manner other things are cached, but the caching does not work great for us so far (and this may be because we are stuck on stage1 due to heavy use of async). I have had comptime lookup table generation and similar stuff crash the compiler or take a long time to run. I fell back to external code generators in these cases.
- belmarca 4y agoYou rely on a compiler to produce an executable, right? Well there are many "phases" that the code goes through before becoming machine code. "Compile time metaprogramming" is having control over one of those phases available from the language itself. Exactly how it is implemented is, well, implementation and language specific. But the fact that you can do it is quite useful. You can not only write programs, you can also write programs that transform or generate programs. The full extent of what you're allowed to do is implementation-dependent. In Lisps, for example, that phase is called the macro-expansion phase (of which there can be layers of) and generally acts as a source-to-source transformation from s-exp to s-exp (possibly with side-effects). Here's an example in a Gambit Scheme REPL: > when *** ERROR IN (stdin)@8.1 -- Macro name can't be used as a variable: when > (when #t #f) #f > (pp (lambda () (when #t #f))) (lambda () (if #t #f)) So you can see the 'when' macro expands to an 'if', and that has to happen _before_ evaluation. So the REPL is in fact a REEPL (Read Expand Eval Print Loop). Very useful.
- P5fRxh5kUvp2th 4y agothink of loop unrolling, but able to be done by the developer instead of exclusively by the compiler. A more natural form of duffs device. https://en.wikipedia.org/wiki/Duff%27s_device https://en.wikipedia.org/wiki/Duff%27s_device
- ben-schaaf 4y agoIt's much more convenient than running an external tool to generate LUTs, which is how you'd do it in C(++). You shouldn't regenerate the database every time you compile, that's what incremental compilation is for. But it is very convenient to integrate code-generation into the build system. What happens when you find a bug in the code/LUTs and you've deleted what generated it?
- dolmen 4y agoThat's what code generator are for. The Go community has a rule of committing both the code generator (for later regeneration and maintenance) and the generated code (to avoid rebuilding it every time).