Starlark Language
github.com
github.com
An Overview of the Starlark language - https://news.ycombinator.com/item?id=40573689
At Google, languages like Starlark are used to build entire software stacks that run at configuration-time. Configuration with a base language, libraries, domain-specific languages, team-specific utilities, conventions, linters, and the whole thing gets built into a package that can be run by some much more general server.
That level of complexity is unnecessary in many places, but the one thing I would take on elsewhere is to pick a good, capable, config language early, and then use it for as much config as possible. Starlark is a good option if you like Python-ish stuff, Jsonnet is also pretty nice.
aiui, Starlark is closer to the internal languages while CUE takes a different philosophical approach (logical language that ensures correctness at scale)
Also I'd argue that there's a good core in Google's config stack: everything resolving to a proto at the bottom, textproto for "raw" representations, and having one language above that. If we could standardise on that it would be good, but of course that's unlikely.
Does Boq use a subset of Starlark? I thought it was Piccolo. I've always assumed Piccolo predated Starlark and that Starlark was essentially the external-first rewrite with some learnings.
Blaze also used to call a Python interpreter to evaluate BUILD files, but this is something I've changed. Starlark is unrelated to Piccolo, it started as an interpreter written inside Blaze. Starlark was created just before we open-sourced Bazel (because I didn't want to open-source the Python preprocessing).
Edit: indeed, Boq's manifest.bzl is Starlark.
Can you explain why?
I'm not trying to criticize anybody here, I'm just curious about the reasoning behind a decision to open source one but not the other.
Bazel started a Python process for each BUILD file to preprocess. If two BUILD files were using the same bzl file, that bzl file would be parsed/evaluated twice. In a graph with lots of dependencies, it was causing a lot of redundant work. It's a bit like #include vs modules in C++: Starlark can evaluate a dependency file once, and provide the result for each BUILD file.
Python can be hard to sandbox, and we got multiple security reports about exploits.
Interestingly, Facebook Buck went through the same way. They originally copied the Python preprocessing approach. When we open-sourced Bazel, the Buck team took our Starlark (aka Skylark) interpreter, and started the migration. See https://buck.build/concept/skylark.html
It used to be a complete mess, but a plan was written a couple of years ago. So there's hope of improvement.
Does it have a debugger now?
I worked there a decade ago and we were trying out the "gcl2" reimplementation that was based on an actual formal specification of the languages. The semantics were subtly different and we couldn't switch our complex config to it quite yet back then. I wonder if the experiment succeeded or you're back on to the implementation-defined language semantics
Since the language and the backend allow to write deterministic functions, we found it's really suited well for automations written as durable functions. We implemented exactly that [3] over Temporal [4], though we also support Python as well among other runtimes.
[1] https://github.com/google/starlark-go
[2] https://github.com/facebook/starlark-rust
[3] https://autokitteh.com, https://github.com/autokitteh/autokitteh
The biggest issue is I want first class debugger support. Language support is fiendishly complex. It's impossible to reason about without a step debugger. printf debugging is insufficient. I should be able to debug as trivially as "attach to process" and F9'ing a breakpoint.
I'm not convinced the restrictions imposed by Starlark actually matter. There's so many compiler toolchains that can introduce non-hermetic behavior that I don't think much is gained from locking down the scripting language. We're professionals here, it's ok to say "don't do that".
Buck2 is pretty rad overall. I kinda wish it was the industry standard. But that will probably never happen because adding support for new languages is so complex it requires a non-trivial team of FAANG engineers to support full-time.
I tried and failed to add Jai support to Buck2. The Starlark rules are too complex and too impossible to understand. Dynamic types are a nightmare to decipher. Compiling programs isn't actually that difficult! Figuring out how to induce the machinery to run the commands I need turned out to be insurmountable. :( Google and Meta are both hamstrung by trying to carry forward a decade+ of old build definitions to new systems. There's significant baggage.
It's still not perfect, and it's very complicated, but people always act like there's no benefit.
A loop over a billion items (for example, due to a bad SQL statement) can hang your system indefinitely, just like an infinite loop. You still need timeouts, or a progress bar and a way to cancel. If running the code costs money, this is especially important.
A static termination guarantee is most useful in a proof language, when you're not going to run the code, just prove things by compiling it. That's when you really need it. You assume the function returns a value, and if there's any way it could fail, the proof is wrong. But performance is irrelevant if you're not going to run it.
For a configuration language, banning recursion might still make sense, though, since it's another way to write confusing code.
$> def f(g): g(g)
$> f(f)
Traceback (most recent call last):
* expression:1, in <module>
f(f)
* expression:1, in f
def f(g): g(g)
* expression:1, in f
def f(g): g(g)
* expression:1, in f
def f(g): g(g)
<many stack frames omitted>
* expression:1, in f
def f(g): g(g)
error: Starlark call stack overflow
--> expression:1:11
|
1 | def f(g): g(g)
| ^^^^
|
Infinite recursion, fun!(this is the starlark-rust repl, which is the only one I have currently installed)
You could easily implement the Y combinator like this, and boom, Turing completeness.
In practice, it doesn't matter, because the way they've been implemented is with limited enough stack space that it's not really relevant. But arguably, that is an implementation detail, and you could implement Starlark with either TCO or growable stacks or whatever. Just like normal Turing complete languages doesn't have infinite memory, Starlark doesn't have infinite stack space. I would strongly argue that the language itself is very much Turing complete.
(I agree it doesn't matter that much in practice)
Yeah, important to note, in practice it is always finite, since if you try to do anything "real" using this trick, you blow the stack immediately. This was mostly just a fun fact about how easy it is for Turing-completeness to sneak in. I seem to remember reading something about how Algol-60 was similar, they intended it not to allow recursive procedures, but permitted function pointers (or something) and you could do a similar thing.
It can be pretty cool as you can pass in arguments and get a different set of services based on what you pass in. I like to think of it is an "exe" for a distributed system.
I’ve had mixed results trying to adopt this though - some people (usually better coders) get it, some like wallowing in their yaml soup
One example: as far as I can tell there is no way to get the Starlark call stack, this is a tool you really want for printf debugging (which is your main debugging tool with Starlark), I can't find it right now but I believe I saw them say that they don't want to give access to the call stack for performance reasons which I find odd - is the bottleneck in slow bazel builds close to be starlark execution time?
Also looking at a non trivial Starlark codebase that didn't mature well, I can't help but wonder if it wasn't better to not give engineers as much flexibility for their build configuration, yes, they'll have some copy pasted configuration snippets that could look tighter with a macro, but you are less likely 5 years later to look at a monster that is slow and hard to debug.
[1] https://bazel.build/rules/language#differences_with_python
If starlark does everything you need (and especially if its limitations are desirable for your use case) then it's the clear choice in my view.