Skip to content(if available)orjump to list(if available)

Comptime as Configuration

Comptime as Configuration

18 comments

·January 10, 2025

kevmo314

Fun story, while at FAANG I was once tasked with making a parameter configurable via flag. This wasn't super easy for various reasons, but I pointed out that it was configurable already: all you had to do was submit a PR to change the hardcoded value in the code and it would update everywhere and that wasn't much different from how we already configured runtime parameters by submitting a PR that updated the config file.

I got a good chuckle out of my coworkers but as I've gotten more experienced there's a grain of truth in that. Actually the main non-joking objection was that deployments are slow so if we needed to, reconfiguring runtime flags was a good amount faster. But that made me think: if deployments and builds are super fast, would we still need configuration in the conventional yaml and friends sense? It could save a lot of foot guns from misconfiguration and practically speaking, we essentially never restarted the server to reconfigure it. From the article,

> Unless you have really good reason to, you probably should not do this for normal options - it makes your library much more rigid by requiring that the user of your library know the options at comptime.

I dunno, actually that sounds really great to me.

MathMonkeyMan

I saw a talk at a C++ conference where the presenter floated the idea of writing a compile-time C++ GUI library for use in industrial equipment UIs.

Why parse markup and generate objects on the fly when there is exactly one UI that will ever be burned into the CNC machine's firmware? Why even bother with an object library that instantiates components at runtime when you know upfront exactly which components will be instantiated and all of the interactions that can occur among them?

At the time, I filed the idea under "C++ wizard has too much fun with metaprogramming," but he was probably on to something.

Another way to think about the idea is "let's invent a new programming language that allows us to express a single UI, and the output of the compiler will be a native program that IS an optimized implementation of that UI."

jiggawatts

I've been thinking about this kind of approach a lot ever since I learned Rust.

A small aside about a personal theory about language design: Every new major language feature like templates or whatever gets "tacked on" without really redoing the fundamentals of how the language works. As in: you could remove it and things would still be fine. For example, you can use C without the preprocessor, it's just a bit clunky. Then, later, sometimes much later a language comes along that really leans into the feature to the point that it can no longer be removed. It becomes fundamental.

The ultimate metaprogramming capability would be to have the compiler phases exposed to the programmer. That is, the compiler would no longer be a binary black box into which text is fed and binary pops out. Instead, the compiler and its phases would be "just" the standard library.

Rust started down this path but the designers seemed to shy away from fully committing. Zig is closer still to this idealised vision, but still isn't 100% there.

Ideally, one should be able to control every part of code generation with code, including C# style "source generators", Zig-style comptime, custom optimisation passes or extensions, custom code-gen, etc...

In a system like this, a single GUI framework could be used to either statically or dynamically generate UI elements, with templating code being run either at comptime or runtime depending on attributes similar to passing a value by copy or by reference.

Look at it this way: We're perfectly happy writing code to generate code. We do it all the time! As long as it is HTML or JavaScript and sent over the wire...

SkiFire13

> Ideally, one should be able to control every part of code generation

The issue with this approach is that the more you can control, the less the compiler can assume. This in turn means that it can check less for you, and tools become harder to write because code analysis often heavily realies on those assumptions. Just to make an example, Zig doesn't (and with the current approach can't) have declaration checked generics.

> In a system like this, a single GUI framework could be used to either statically or dynamically generate UI elements, with templating code being run either at comptime or runtime

I feel like this is overly optimistic. Some things will always be runtime-only, even some very basic ones like allocating heap memory. You can likely sidestep this issue and still precompute a lot at compile time, but then chances are this way of computing will be less efficient at runtime. In the end you'll likely still end up with different code for comptime and runtime just because of specific optimizations.

XorNot

I'm trying to figure out why this wouldn't be much more widely applicable? I.e. all mobile apps are basically a finite series of screens with limited actions as well.

kevmo314

I think it's somewhat hard to get the API right. This is what React-based static-site generators are but people love defeating them by introducing runtime dependencies and configuration.

bsder

You can't continuously release a mobile app thanks to app stores.

So, a lot of runtime stuff is papering over the fact that your submission to Apple takes too long to resolve.

DanielHB

Sounds a lot like static website generation that a lot of JS frameworks do.

There are a lot of pitfalls with this approach, but for a subset of problems it is very good.

mongol

You definitely have a point. We need to distinguish between in-house developed IT-systems, and software sold as a product. They are quite different. For the former, whether a file is a source file or a configuration file often does not really matter. Like you say, they are managed in version control, some build magic happens and they are deployed. Differences between environments, automated tests etc matter, but with that in mind, there is absolutely room for simplification in many cases.

For the latter, it is more clear. The developer develops the code, the user changes the configuration

nasretdinov

IMO the main issue with this is that it means you'd have to patch the production version, and it might be different from the current (master/main/develop) branch, so you'd also need to backport your fix. Keeping configuration separate allows you to avoid that.

cryptonector

If you're using Kubernetes / Argo then who cares. There's no backporting, there's only moving forward. If the configurable code in question is only ever used from such deployments then making the configuration static and compile-time makes some sense, and saves you having to write code that handles configuration at run-time. One might like to think that this sort of code might get run in other contexts too, so one might tend to prefer flexible, run-time configuration, but if you know that code will only run from deployments then you might as well not waste the effort on run-time configuration.

chikere232

Releasing more often, keeping master/main/trunk deployable, and not having a develop branch or long lived feature branches solves a lot of that.

Of course that sometimes needs some kind of feature flags for bigger changes, which is a configuration option too, but at least the stable state of the code is simpler and not a nest of code + config that never really changes.

rad_gruchalski

And we get ci/cd so any flag change means a pr anyway…