# Prone

Name subject to change. I've also been considering MCMPL (Maximally Constexpr Meta-Programming Language), which is highly descriptive, but not necessarily as fun or ambiguously saucy as _Prone._ Especially since this is a language that compiles to source code in other languages, with no intent at portability, so `prone4js` would have a lot of conceptual common ground with `prone4c`, but different primitive types.

Anyways. Prone is a language that brings a consistent set of higher-level concepts and constructs for fluent and flexible metaprogramming in a variety of environments. It's meant to make certain types of big, painful problems easy, particularly in the game and web development spaces. It takes big swings with potentially controversial tradeoffs.

## Constant by default

Normally, you want a compiled language to _compile quickly and in a reliable amount of time._ This is a goal that most people take for granted. Prone trades this away on purpose for something else entirely: that everything which _can_ be evaluated at compile time, _will._

The most obvious ramification here is that your builds will be slower and your programs will be faster. That's a pretty straightforward engineering trade. But there are other noteworthy, intentional consequences.

## Flexible at the build phase

Prone is architected primarily as a dynamic, interpreted language running on the builder's machine, with access to filesystem APIs and such. You can dynamically define structs, unions, even make consistent aliases for specific primitive(-y) types:

```
atom_int = platform::number.integer.bits(:at_least, 32).atomic;
```

There's far _more_ you can do in this interpreted phase than in the compiled product. So when you compile, part of what the compiler is checking for you (alongside type checking) is that every bit of dynamic-only logic has boiled away, leaving only the stuff you're allowed to do at end-user runtime. If there's some inextricably dynamic logic left after all the dynamic evaluation (for example, trying to name a struct field after an ARGV argument), that's a compile error.

This has a lot of cool implications, like the fact that if you know a regex at compile time, it will automatically be statically compiled into a state machine, and no state machine compilation code will exist in your final build _unless_ you need to build one dynamically. Consider yourself tree-shook.

## No Make necessary

Compilation is just another feature exposed to the dynamic interpreter as a callable function. That's the way it works. This means that your "build system" is just... Prone!

```
# build.prn

int = platform::number.integer;
argv = array_type(platform::pointer(char));

read("foo.prn").compile([
  [:main, int, argv],
]).save_to("foo.c");
```

I can easily imagine people creating libraries and frameworks for elaborate use-cases and build systems, especially when it comes to things like Make-style memoization. The fact that you get something basic and frequently adequate out of the box is really nice though. This example could be pretty trivially modified to print the compiled-to-C results to STDOUT, or run `gcc` on them with appropriate flags.

This is also great for embedding resources into a final product, for example, compiling SASS to CSS and incorporating it into a page template before we ever get to runtime.

## Plain old COWs

There's a lot I could say about Prone's data model. And I will, but it'll have to be split into multiple sections! The best starting point is the memory model for plain data. Prone is a language opinionated against OOP. Most of your code will be working with plain, "inert" data.

Even plain data can be large. In many languages, you'd have to think about whether to pass by reference or by value, when each has hazards. Passing by value is often unacceptable for performance reasons, while passing by reference can be a minefield of distant actors mutating data from under you. There is a sensible one-size-fits-most common ground to be found though: Copy-On-Write semantics whenever you pass into an inner scope.

It's pretty fair to think of this as "pass by value, if it was fast," at least for most purposes. The compiler is often able to see whether a piece of data is modified by a callee, never used again in the caller, etc. So in the end product, we usually statically know whether we need to _clone_ or _move_ when preparing arguments in a caller. It _is_ possible to have runtime dynamic COW logic. But for other reasons of language design, we can usually boil that off with static knowledge.

## Handles

Not everything can be accomplished with plain data alone, especially when you're interfacing with other libraries in your target language. So the other big part of Prone's data model is "handles," which are uniquely/linearly typed black boxes. These have some restrictions compared to plain data.

1. You can only interact with them (directly) via functions written in the target language.
2. They have always-on move semantics. If you pass a handle into a function, and it doesn't return the handle back to you in some way, it's _gone._
3. You can't pass them between dynamic and compiled code.
4. _Some_ handles implement `clone()`. This isn't required, and doesn't always make sense to implement. Totally opt-in.

These rules help you keep a tight lid on your reference-y objects, either preventing or gatekeeping shared ownership. You can use them for something as small as a socket or as big as an instance of a physics engine.

## Multiple dispatch

They call it OOP because it was a mistake. There are no methods in Prone. We do _functions_ here.

That might raise your eyebrows, because these code samples sure use a lot of dot syntax! Well, that's just syntax sugar:

```
add = (x, y) { x + y };

# These are equivalent!
example1 = add(2, 5);
example2 = 2.add(5);
```

This is makes it convenient to chain operations without nested function call hell. But what does this have to do with multiple dispatch? Well, functions can be redefined, referencing the old definition using the `super` keyword.

```
add = (x, y) { x + y };
add = (x, y) {
  if (x.is_a(string) && y.is_a(string)) {
    x.concat(y)
  } else {
    super(...arguments)
  }
};
```

The language syntax is a research project in general, but this part is particularly. But hopefully the main idea is clear: that we transparently leverage build-time reflection on a regular basis, and it's one of the main things that _must_ boil out through optimization in order for a compilation to succeed. `super` fails in a consistent way if you try to call a super that doesn't exist, which will catch at compile time (type specialization specifically) if you pass an invalid set of args.

Part of the reason this is so up-in-the-air in the details right now, is because there's probably a sweet spot of cleverness for leveraging match constructs, variable name bindings, different arg counts for different definitions, subscope syntax (not quite closures), etc. I want to get a lot of mileage and consistent reuse out of as few core ideas as possible, which means spending a lot of time experimenting.

## Errors

Errors are data, and while they may have different types, those types are all fundamentally:

1. A `printf`-style message string,
2. Multiple arguments that can be referenced in the message string.

For example:

```
http_404 = error_type("404: File Not Found (path: %s)", string);
my_failure = http_404.new("/foo/bar");
```

This helps you avoid constantly reinventing the wheel. And thanks to multiple dispatch, you can easily extend an error type with further functionality, like in this case where you might care about being able to get a numeric status code:

```
status_code = (err) {
  if (err.is_a(http_404)) {
    404
  } else {
    super(...arguments)
  }
}

assert my_failure.status_code == 404;
```

I really might need multiple dispatch syntax sugar. Like, it's hard to argue with this:

```
status_code = (err: http_404) { 404 };
```

Which would be the same thing after desugaring.

# An illustrated tour of why Prone prevents implicit casting in JS

This is useful, because it connects a lot of concrete ideas together. The JS platform has some pre-provided code, which you can think of kinda like a prelude or something. Because these exist at a boundary with the target language (think, `extern "JS"`), they have explicit concrete types for every parameter and the return type.

The **ONLY** way to call JS code (or anything natively written in your target platform) is with a boundary function. This is an important guardrail, but it's made convenient by the fact that the interpreter and compiler will automatically turn anything in the `platform::native` namespace into a native call.

```javascript
// JS Prelude-y stuff

// String concat
function add_string_string(a /* string */, b /* string */) { return a + b }

// Numeric add
function add_num_num(a /* num */, b /* num */) { return a + b }
```

But part of the platform code is in Prone itself, where types are more dynamic than they are in interface code:

```text
# implicit implementation:
# op::plus = () { dispatch_err.new("Dispatch error for op::plus(%r)", arguments) };

# Create a multiple dispatch out of the native functions
op::plus = (a: string, b: string) { platform::native::add_string_string(a, b) };
op::plus = (a: num, b: num) { platform::native::add_num_num(a, b) };
```

Note that we don't have a dispatch established for string+num, so that will be caught as a dispatch error! This also touches on how operator overloading works, as operators are just sugar for calls to the appropriate functions in the `op::*` namespace, so these two lines are equivalent:

```text
added_with_sugar = 3 + 2;
added_sugar_free = op::plus(3, 2);

# Returns a dispatch_err!
add_failing = 3 + "two";
```

TODO: Expand this example later to show how Prone uses union inference instead of Rust-like `Option` or `Result` types.

# Status

I thought it would be easier to prototype in JS, but I think it's going to be more useful to start in C in order to intentionally run into the hardest stuff first.