I have grown pretty negative opinions of “using a programming language as a config language” after semi-recently having to spend a lot of time screwing around with someone’s Gradle Groovy abomination. Not even to mention how quickly Nix can get annoying.
I have to ask…why do people want a Turing complete language to configure stuff? I use Sway personally, which is very configurable but for any even remotely complicated logic I shell out to a set of Rust programs.
So you don't like using programming languages as config languages, so you:
1. Use a declarative-style format for config, which is fine, I guess; 2. ... and when that inevitably fails, you... use a programming language?
Some people -- not all, but some -- may prefer to skip the first step and go straight for the second option. Particularly if it's a complex thing indeed like a build system and not, y'know, configuring your blog.
Lua was specifically designed to be a configuration language https://www.lua.org/history.html
Lots of people start out with a non-programmable config format. But, as their situation becomes more complicated, they end up shoehorning in programmable-ish features until they realize they are running straight into https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule and decide to do it properly.
It is good to keep a configuration simple enough that it doesn't need to be a programming language.
The absolute moment you find yourself thinking of putting conditional logic, or string substitution, or anything like that, in your pure config format, switch to a real programming language, before you end up managing several times the complexity of one with only some of the benefits. No need to end up contemplating some sort of for-loop macro with another key referencing the loop variable, when you can just have a for loop.
I find TOML, in addition to being very readable, to be great at automatically restricting config complexity by making nested lists of tables miserable. If your config sucks at being TOML, it should be JS or Python. (Not a fan of Lua.)
Speaking personally, there are essentially two distinct reasons I like having programmable config languages:
- For personal application config (window manager, shell, text editor, etc), I find I often want to have site-specific configuration, but I also want identical configuration files at every workstation. My laptop probably needs slightly different keybindings than my desktop, my $PATH and text editor needs to be configured a bit differently for work than for personal use, etc. When the configuration is done with a real programming language, this is always straightforward, when it's done via a json/yml/toml file, it's usually a huge pain in the ass.
- You want templating for your configuration, to improve readability, reduce typos and configuration drift, &c, but if you give programmers an inch with any sort of templating system, they will eventually take a mile and figure out how to smuggle Turing-completeness (or, at least, for-loops and variable binding and probably closures) into their configs. If you have a real programming language, this is fine, because the programming language was designed to make loops and conditionals and function calls readable, and probably has decent tooling to boot, but if they're doing it with, say, Mustache-templated YAML, you will have an unreadable mess that is extremely unpleasant to work with.
The usual answer is "config macros", because if you don't give people something Turing complete they'll just m4 their way into Turing completeness and that isn't really an improvement. (Starlark in Bazel, etc.)
Half of me agrees with you. As someone who used to tinker with neovim all day I've had to learn just because you can program the problem doesn't mean you should and stick to a relatively minimal config.
Still, its a valid design choice with different design decisions compared to sway. But that's the point you get to choose.
Hyprland is much more "build your workspace with legos" so having a full lang like lua makes sense to properly integrate the various pieces.
All these window managers have a Turing complete configuration language, because you need to do things in response to events (keyboard hotkeys etc). It's just that for most of them, the "Turing completeness" happens through passing around strings with shell snippets. Might as well be a Lua callback function.
Because there's a never-ending stream of plausible use cases where it just missing one little option. Just look at the web: people have done all sorts of horrible stuff in Javascript to make up for missing HTML / CSS properties.
At a certain point it becomes incredibly tempting to just give up on playing whack-a-mole and let the most demanding users do it themselves using a proper programming language.
I have switched from FVWM to awesome because of Lua. Many years later, I have switched to i3 because it's not using a programming language for configuration. It's fine to have a configuration language if you have time to maintain, but your configuration can become quite complex and difficult to evolve. And it is also far more subject to breakage when upgrading.
I think I've found balance by using a Python daemon as a "companion" for my i3 configuration. It listens and react to event. It enhances the configuration, so when it's broken, it's not a big deal. And the interface exposed by i3 is smaller, so less risk of breakage. https://vincent.bernat.ch/en/blog/2021-i3-window-manager#i3-...
> I have to ask…why do people want a Turing complete language to configure stuff?
I don't understand this either. IMO, the current SOTA tool for configuration is CUE, because it is the only one that lets you write schemas for arbitrary refinements. I haven't used it myself, but my understanding is that everybody should be writing configurations in something like this instead of YAML, TOML, HCL, or a custom DSL with no tooling. And yes, definitely not in a Turing complete programing language.
> I have to ask…why do people want a Turing complete language to configure stuff?
What's the alternative? Petition the maintainer to add every little feature any random user might want? Or just do it yourself in a few lines of code?
Case in point, when I was still using BSPWM I did no like how if there was only one window on the desktop it would still get an outline. So I wrote 14 lines of Awk (BSPWM lets you use any language) to get what I want. This was only possible because BSPWM gives me all the primitive building blocks and lets me go wild with them. Sure, if all you want to do is set a few colors and numbers that's overkill, but any power user will sooner or later find himself thinking "oh, if only I could do X when Y unless Z".
I don't dislike it but I do think it's interesting that a number of minimalist WMs seem to have converged on the idea of code as config. I have used AwesomeWM for years and love its Lua configuration for providing infinite flexibility. When I was recently looking for something like AwesomeWM but in Wayland I toyed with the idea of moving to something with a simpler config format (and Hyprland was one of the options I considered). I can see the benefit of the simplicity but I guess for the kinds of users that use these WMs, maximum configurability probably trumps everything else. (Currently I am using SomeWM which is just an attempt to get something as close as possible to AwesomeWM in Wayland.)
Personally I have grown a bit frustrated with Lua as a language, but at the same time can see why it would be a good choice for this use case.
It smells like a variant of inner platform effect. Where in order to save you from programming the the config is super flexible, What is missed is that you are still programming only now you have to do it in some weird context.
At least in this case it is an actual programming language instead of XML or YAML like so many of them.
An alternative slightly more charitable interpretation is this is the engine/gamelogic pattern. But the difference between the two is more subtle than it naively appears depending largely on proper layer segmentation.
if you get into a config system that's complicated enough, and has modularization or inheritance, you'll know that the semantics of "remove this" "add that", override this, default to this, this stuff is hard to express in most config languages, or worse, you start enforcing application level semantics. when you have turing complete language you just ... do those things. we reduced the configuration data of a tool suite once by 90%+ by moving from json to python. there was a tremendous amount of duplication and overlap, with tons of TINY exceptions and variations. it was so hard to see those differences, brittle to maintain, things would get stale, ... moving to python you basically had the "base" thing expressed in one file, and a bunch of tiny delta files encoding the exact deviations. All those deviations were of the forms I described above. Adding a thing to a list. Removing a thign from a list. Overriding a whole list. Do a thing if not already done. And so on. It's also then easy to build all kinds of diagnostics, validators, etc, right into the same framework, without having to munge the data into some other form first.
I would agree that turing-complete config is more trouble than it's worth when the config is something you might need to reason over, such as a build definition in Gradle. But for something like a window manager config where everyone's config is a snowflake, it's kind of ideal. Horses for courses and all that.
It depends a lot on the config language. Gradle has the uncanny ability to be a "worst of both worlds" option, where there are not many upsides to its using a full language for its configuration. Build configuration is also a space where people and even some tooling seem to forget about good engineering practices, giving birth to fragile builds that people are afraid to modify and improve.
I think the progession is like 1) Oh, I can break stuff by changing a config value - it doesn't protect me. 2) Oh, I didn't think X would need to be configurable. 3) With code I can colocate the values with the code that uses them - easier to understand, and I can "configure" anything I want. 4) Why are we using configs?
Yeah I think there are two basic routes that work. Either you have a declarative configuration, or you go the route of Emacs and make the entire thing programmable.
Turing complete configuration languages suck and makes everything slow, janky and difficult to reason about.
I think INI-style config is one of the few good things that came out of the Microsoft/Windows ecosystem. Shame that it is so underspecified. We have TOML, of course, but I think for stupid config even that is too much.
The intersection of INI and TOML would be perfect.
I like the ability to introspect the system. I have a lua LSP, when I edit lua config files, I know all of the fields of the config I am editing, and I know how they behave (without randomly trying things)
Because it almost always devolves into that anyway. Every yaml system I have ever worked in winds up with some layer on top to “generate the yaml” or some other such madness.
I never really felt like scheme got annoying when configuring guix. I’m half certain most of my configurations for Guix services were just yaml configs with s-expressions
I think it's only really useful for things you'd want to be able to control programmatically, like text editors or in this case window managers
I fully agree with that.
Past experience has shown what while Turing complete config is powerful, it is also a double edged sword and I would be wary of using it.
> I have grown pretty negative opinions of “using a programming language as a config language”
Same here. I like the openbsd approach of small DSL (pf, doas) or just simple ini-like key-value config (sysctl, rc.conf). If you want more, just patch the software and be done with it.
A programming language is OK if the intent is to build a platform (Emacs, Vim) but for something that is purely an application software, it's very much not necessary.
>I have to ask…why do people want a Turing complete language to configure stuff?
because it lets me configure programmatically. Practical recent example, I use Wezterm which uses Lua as a config language but I tried out Ghostty and because I use all operating systems regularly I have a lot of 'if this then that' in my Wezterm config, different default shell, different key bindings etc. With Ghostty my only option is to maintain different config files per machine.
And what's worth pointing out is that Ghostty does have an operator for this('?' I think) which means there's already a bit of programming in it, except what always happens is that software with declarative config files invent their own scripting an templating languages as more and more people demand it, so just give me lua or lisp from the beginning and we're good.
A window manager / compositor is one of the few things where I can see this making a lot of sense. The alternative of using an IPC socket for everything would be more complex and require users to reinvent the wheel all the time. Also it makes low latency synchronous reactions to events really hard to implement well.
Seconded. Your configuration should be a simple declarative language with no Turing-complete escape hatches. Turing-complete configuration languages are the devil. That goes for build systems too: if you absolutely need Turing-completeness in your build system (my condolences for the hellscape you inhabit), use a separate task runner that does its arbitrary nonsense before dispatching to an underlying build system with declarative configuration.
I want a programming language as soon as the program benefits from conditional clauses or subsystems. So if the will and know-how is there, would be great to have it from the start.
I was pretty happy the day I could move some Jenkins jobs to a Groovy script. I've worked with HCL significantly, but once I learned the idiosyncrasies of Pulumi, scripting infrastructure via a programming language was a better experience (good IDE autocomplete simplified things), though it requires more discipline.
Even without working with the Nix scripting language I'll dismiss it. When I looked at it was some kind of bastardisation of Bash with Haskell ideas. A hobby project idea at best, yet forced upon all Nix users. Guile Scheme is a nice small language, and Guix uses that, and I think that is a better approach.
If you look at hyprland config, you can see why they went with it.
Much like vim/neovim config, people want to do things like register a callback that fires to determine the window title and things that are much more advanced than that, like layout management.
When I was using sway, I was using its IPC or worse (https://github.com/johnae/persway) to do advanced things, and Lua scripting in the first class config would have been a much better UX.