Originally conceived as a better language fit for implementing the Layover package manager, Prone is an experimental programming language that attempts to explore some of the recent trends in C++ language evolution and Data-Oriented Design. Its philosophy holds a lot of promise for a diverse field of use cases, particularly video games, audio synthesis software, web servers, and statistical/scientific analysis. It's my earnest goal to make real projects of value easier to achieve for average people, by offering a language that's relatively friendly and safe out of the box, but which has a higher performance floor than most dynamic languages, and an unbounded performance ceiling.
## Constant expressions

To understand what makes Prone different, it helps to take a look a C++ keyword that was a huge inspiration, to the point that the original prototype name for Prone was MCPL: the _Maximally Constexpr Programming Language_. Here's a real reference example directly from [cppreference.com](https://en.cppreference.com/w/cpp/language/constexpr.html), with some edits for conciseness:

```cpp
#include <iostream>

constexpr int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

int main(int argc, char const **argv) {
  std::cout << "4! = " << factorial(4);
}
```

This is almost a normal factorial function in C++, except for the `constexpr` keyword. Without that keyword, a normal C++ compiler would probably[^1] generate machine code for the `factorial` function, and then when you run the compiled program, it would call the `factorial` function. You know, business as usual for compiled languages. With `constexpr`, the _compiler_ does the work of computing `factorial(4)`, and then the emitted program would actually behave as if you'd written _this_ C++ code instead.

```cpp
#include <iostream>
int main(int argc, char const **argv) {
  std::cout << "4! = 24"
}
```

No, really. There's probably a string `"4! = 24"` in the output binary somewhere.

Moving computation earlier (or in nerd jargon, "leftward") is a really powerful optimization technique. Do the work once on a build server somewhere, and then everybody who uses your program gets a little time savings, because that's work that doesn't have to happen at runtime. At big distribution scales, like Firefox etc., those multiple tiny savings for multitudes of people really add up! It's also a bigger deal than it looks, because optimizations chain into each other. A non-`constexpr` version of our example would have to do two printing operations, one for the string `"4! = "` and one for the integer number `24`, because it just doesn't know what four factorial _is_ until it gets to that part of the code. But if you know that `factorial(4)` is just `24` _at compile time,_ that's enough information to make one bigger string `"4! = 24"`, and then that's just one print operation that has to happen at runtime.

The final bonus of constant expressions is that if you move computation leftward, you move bugs leftward too, which means catching them earlier and cheaper. Consider the impact of a bug that never leaves a developer's laptop or even makes it into a `git` commit because it's caught so early, compared to the [$18.5m Mariner 1 spacecraft failure](https://en.wikipedia.org/wiki/Mariner_1), [the $460m dead code glitch that killed Knight Capital Group](https://en.wikipedia.org/wiki/Knight_Capital_Group), or perhaps most infamously the original Murderbot: [the Therac-25](https://en.wikipedia.org/wiki/Therac-25).

So compile-time evaluation is great for speed and safety, at the cost of a little more build time. With some exceptions for things like benchmarking, it's generally desirable to do as much stuff at compile-time as possible. And here's where we hit the limits of C++: it's an existing language, backwards compatible with C, and with decades of its own growth and baggage. One does not simply retrofit code execution into an industry-grade C++ compiler. There's a lot of code that just can't be run at compile time, or values that can't be handed off from compile-time to run-time, and while those restrictions are slowly relaxing with every new C++ standard, this _is_ a game of catch-up, and often at odds with people's security assumptions[^2]. You could say similar things about the new and growing reflection support in C++: it's a godsend for developers with existing investment in the language, it's doing pretty well within the heritage restraints it has to live with, but there will always be some feature horizon that you'll just never get past without the freedom of working in a new, blank-slate language.

So what if there was a new, blank-slate language that was really good at doing work at compile-time and generating C code? That's Prone.

Prone operates firstly as a dynamic, interpreted language that can read files, interact with user input, make or unmake types and functions - anything you're used to being able to do in a dynamic, interpreted language like Python or JavaScript. And it'll _keep_ working in this mode until there's no more work it _can_ do. Only at that point will it dump its state as a bunch of C source code that can be compiled with any modern optimizing compiler, creating a fast static program that picks up where the interpreter left off.

Importantly, this cutoff point is determined by data dependencies. Consider Rust's excellent `regex` package, primarily developed by [@BurntSushi](https://github.com/BurntSushi). Literally one of the fastest regular expression implementations on the planet at the time of this writing. Regular expressions have to be compiled to an in-memory format before they're used, whether that's native machine code, or some data structure, etc. On a theory level, if you know the regex string that you want to compile at _program_ compilation time, then your regex compilation can happen as part of program compilation, and then you don't need to ship a regex compiler as part of your program's runtime logic at all. [In practice, this is infeasible for the foreseeable future.](https://github.com/rust-lang/regex/discussions/1076) One of the design litmus tests I have for Prone is that the compile process should _figure out for you_ whether you need regex compilation machinery in your program, entirely based on whether any of the input strings are only known at runtime. If they're all known at compile time, you only need to ship a tiny fraction of the regex library! And if a regex string is _generated_, but we still have all the ingredients to generate it at program compilation time, you get that classic chaining of optimizations that come from moving work leftwards.

This also goes for serialization logic, where we can generate very optimized code for converting user data structures to and from JSON, and we can do this in a very readable way thanks to reflection. A proper JSON library for Prone would, theoretically, generate encode/decode functions based on reading the structure of the types. These generated functions would then be immediately usable as interpreted code, but have the specificity to bake down to some very tight C code, which would then compile to extremely specialized assembly code. It's not just important for this kind of thing to be possible - [people are putting in heavy work to do it in C++ today](https://www.youtube.com/watch?v=Mcgk3CxHYMs) - it's important for this kind of thing to be _easy._

## The Proneciples

So now that you have the context for "why is Prone", we can talk about the "how is Prone" - what other properties fall out when _maximally `constexpr`_ is the primary design goal, but you have secondary goals of user-friendliness, and [DOD](https://en.wikipedia.org/wiki/Data-oriented_design) is a decent approximation of your idea of hygienic programming?

 * **Data is transparent:** private fields have no place in maintainable software.
 * **Data is pure:** inert things are safe to touch, and software is about touching data.
 * **Data is serializable:** because it's pure and transparent, we can always pass the baton from the interpreter to the generated C source code.
 * **Data is structured:** types let us group together assumptions in ways that aid humans and machines alike.
 * **Data is reflective:** types are readable information that logic can act on.
 * **Functions are data:** with the exception of primitives, you can convert logic into structures and back again. You can generate data, so you can generate code.
 * **Function execution is not externally pure:** software exists to interact with its context.
 * **Function execution is contained:** when you call a function, it can't do anything you don't allow.
 * **Arguments say "moo":** data behaves in a copy-on-write (COW) way when passed into a function. If the callee modifies data, they implicitly copy it first, leaving your version alone.
 * **Arguments dictate behavior:** Return values depend - even structurally - on the values of params.
 * **Pack light to go anywhere:** the core language should be small and portable to exotic systems.

This list is subject to expansion and refinement, because Prone is (among other things) a research language, to discover what's possible outside of well-explored territory.

So, if you found this site because you saw my shirt/sticker design:

![[prone-black-bg.png]]

... now hopefully you have some context for what it means! One of the signatures of the language is that it combines short-range mutability (which makes almost all programming easier, especially if you like performance) with long-range immutability (which prevents different parts of your program from stepping on each other as you scale up). _COW across function boundaries_ is a compromise position that's easy for programmers to understand, reason about, and stay safe.
## Gaming as a case study

I mentioned video games earlier as a use case that Prone is particularly apt at, and this is honestly one of the better examples I could pick as a tour of why this combination of features and stances adds up to more than the sum of its parts.

Games tend to be heavy on media assets like meshes, graphics, audio, maps, and dialog trees. Often, the raw files need multiple steps of processing before they can be used in game, which you can do in the same programming language as your game itself. In fact, it's a bit more than that. Prone includes standard library functions specifically designed so you can use Prone scripts as a build system for code and assets alike.

It's great at embedding data. If you want to ship your game as a single file, it's pretty trivial, and will probably allow your game to omit some file loading code. You even have the ability to do things like JPEG decoding at compile time, baking raw image data into the program. These options won't make sense for every project, but they're trivial to try out.

Prone lets you generate code based on data, and load data from files. So you can have various enemy behaviors defined in JSON, and translate those into runnable code during the interpreter phase, so it becomes optimized machine code in the final product.

Have you ever needed to generate lookup tables? There's a bunch of conventional options, like writing them by hand, writing a tool that generates source code, lazily generating them at runtime, etc. That last option is particularly nice, because you're generating a normal in-memory data structure with normal code, but you do end up manually or automatically having to handle thread safety. Prone, by being maximally `constexpr`, will bake your LUTs into the static part of your program binary unless you go out of your way to prevent that.

Prone is a very friendly language, with automatic, predictable memory management. If you're used to something like Python, Prone will feel pretty familiar, while giving you a performance boost for free, so 90% of your game can be written with your brain fully relaxed. But the fact that it compiles to (intentionally readable) C code means that you can dive a layer deeper when you need to, see that things aren't so scary under the hood, and diagnose issues in the hot spots you need to. Prone integrates tightly with C, allowing you to write parts of your code in C if you need to, and use third party libraries seamlessly. So the default speed is good, but when it's not good enough, you're equipped to push those limits without the language pushing back against you.

The focus on data purity and intentional lack of classes proactively prevents a lot of the mess that game programmers are used to as a status quo. If you've ever been left scratching your head, saying "I used all the design patterns I'm supposed to, but now I can't follow what's going on or why it's slow," I hereby set you free from any pretense that object-orientation will make your life easier. Pure data transformed by functions _is_ how professionals write high-performance game code in real life. There's only so tangled you can make things when your data is prohibited from being excessively smart.

There's another benefit too. Usually, if you want an ecosystem of features that actually play nicely with each other, you need to reach for an off-the-shelf game engine like Unity, Unreal, or Godot[^3]. General-purpose engines tend to have parts that you have to put up with or work around or turn off, and picking an engine is an art of big picture compromise. In an ideal world, you would make a per-game engine by taking the exact libraries you need off the internet, and snapping them together, which ought to be easy. Right now, it isn't easy: functionality is caged up in methods and private fields, so Library A has to know a lot of specifics about Library B to integrate with it. Just like we have format standards for data on disk, we need format standards for data in memory, so that libraries can interact with each other as long as they support common formats (or some game programmer is willing to write data conversion functions, which are easy). Prone's pure, transparent, structured data is perfect for defining and mediating between these standards. I think in 20 years, general-purpose engines will feel like a historic quirk, but people will be having arguments about preferred in-memory formats, because _those_ will be the centers of ecosystems.

When you _do_ need to optimize, you can write (or reuse) a simple, readable, obviously-correct reference implementation of whatever needs to go faster, maybe with a few traditional tests of its own. Then, you start making your optimized version, either in Prone or C. Because data is pure, you can compare the two by plugging the same inputs into both, and seeing that they agree on the output. The standard library provides automated testing tools for fuzzing (generate random test cases trying to find a failure, treating it as progress when new code runs in any of the implementations), and mutations (seeing if your test case corpus is big enough by making changes to your implementations and seeing if the existing tests catch it) to rapidly grow a rigorous test corpus and prove that the fast version really does get exactly the same results as the easily-validated reference version. You can even benchmark the speed difference. This is expected to be an extremely common pattern in the way people use Prone, even used within the test suite _of_ the standard library. Reference implementations are the gold standard that allow us to use faster, result-equivalent code with peace of mind.

By the way, saving and loading logic is easy to write when you have pure, serializable data as your internal lingua franca. Just sayin'. Debugging is a lot easier too. You can literally print the whole state of the program with no hidden corners.

Games exist in a particularly demanding overlap between a need for flexibility and experimentation during development, and the need for performance in the finished product. Prone covers both nicely. It feels like a scripting language, performs like a compiled language, and pulls out every stop to make sure that you can navigate complex problem spaces without painting yourself into a corner.
## Current development state

Prone is under active development, and far away from even an alpha release. There are ongoing syntax and design problems being solved. The documentation is nonexistent. That said, [the code is on Sourcehut](https://git.sr.ht/~maddiem4/prone). I work on it in my spare time, and post about my effort regularly on my blog, [Fuller Stack](/blog/fuller-stack).

[^1]: That's a little bit of a fiction for the sake of explanation. For simple functions like this example, modern C/C++ compilers are very intelligent and aggressive about optimization, and would usually compute the factorial at compile-time even if you don't ask for that. The key word is "simple" - more complex examples of compile-time evaluation often require the `constexpr`/`consteval` keywords, or for things like IO, are simply impossible. Generating functions and structures conditionally is somewhere in the middle - possible with Templates, but the code required can get pretty hairy, depending what you're trying to do.

[^2]: You could, with some caveats (ask Matt Godbolt), assume once upon a time that compiling code was pretty safe - it's _running_ the code that might get you into trouble if you don't trust the author. The more you allow code-running to be part of the compilation process, the less safe it is to compile untrusted code.

[^3]:  Godot in particular gets points from me for being open source and highly customizable, which helps mitigate the problems that big engines tend to have.