LanguagesArchitecture

We're happy to announce that the Vale 0.2 beta now has const generics! Generic parameters can contain not only types (like the T in List<T>) but also integers, booleans, and other interesting substances.

This might be familiar from other languages, like C++ and Rust. However, Vale's taking them in a different direction, to enable something called the compile-time spread operator, which will serve as a useful metaprogramming tool for Vale.

This article gives a quick overview of const generics, and then where Vale plans to take them.

Const Generics

Before const generics, we often had a lot of similar classes:

vale
struct Vec2<T> {
elements [#2]T; // 2-element array of T
}
struct Vec3<T> {
elements [#3]T; // 3-element array of T
}
struct Vec4<T> {
elements [#4]T; // 4-element array of T
}

It would be nice if we could pass that 2, 3, 4 in as a generic parameter like we did T!

Enter const generics, which enables just that.

vale
struct Vec<N Int, T> {
elements [#N]T;
}

They're used like Vec<2, int>. Much better!

Besides types and integers, we can also have generic parameters that are:

  • Booleans
  • Strings
  • Functions
  • Mutability (imm vs mut)
  • Ownership (own, borrow, share, weak)
  • Location (inl, heap)
  • Variability (vary, final)
  • Type Lists

Compile-time Spread Operator

That last one is particularly interesting, as it allows us to implement tuples in Vale:

vale
struct Tup<T RefList> {
_ ..T;
}

_ means the field is unnamed, and ..T means "use each of these types".

That .. is known as the spread operator. It can basically be thought of as the "compile-time for-each loop".

(int, bool, str) is syntactic sugar for Tup<RefList[int, bool, str]> which expands to this:

struct Tup {
  0 int; // an int named 0
  1 bool; // a boolean named 1
  2 str; // a string named 2
}

The Spread Operator's Future

The above works today, in version 0.2. Now we'll show you a sneak peek of where we're heading with this delightful little operator.

We want to be able to use it for function arguments, and in expressions. For example, we could use it to implement a zero-cost variadic println function:

func println<T RefList>(args T..) {
  ..print(args..);
  print("\n");
}

If we called this with println(4, " hello ", true), it's as if the println function contains:

func println(args0 int, args1 str, args2 bool) {
  print(args0);
  print(args1);
  print(args2);
  print("\n");
}
stdout
4 hello true

In the previous snippet, the prefix .. (before print) marks the beginning of the "loop", and the postfix .. (after args) specifies what should change in each iteration.

Spread Method Call

When combined with UFCS, another interesting capability emerges, which we'll call the spread method call. The above snippet can be rewritten as:

func println<T RefList>(args T) {
  args..print();
  print("\n");
}

It's fascinating how one little symbol can enable such a powerful capability!

Thanks for visiting, hope you enjoyed it!

In the coming weeks, I'll be writing more about our "Fearless FFI" plans which will help us more safely use external C code, so subscribe to our RSS feed twitter, or the r/vale subreddit, and come hang out in the Vale discord!

If you found this interesting, please consider sponsoring us:

With your help, we can write this kind of nonsense more often!

- Evan Ovadia

Side Notes
(interesting tangential thoughts)

We're looking for sponsors!

With your help, we can launch a language with speed, safety, flexibility, and ease of use.

We’re a very small team of passionate individuals, working on this on our own and not backed by any corporation.

If you want to support our work, please consider sponsoring us on GitHub!

Those who sponsor us also get extra benefits, including:

  • Early access to all of our articles!
  • A sneak peek at some of our more ambitious designs, such as memory-safe allocators based on algebraic effects, an async/await/goroutine hybrid that works without data coloring or function coloring, and more.
  • Your name on the vale.dev home page!

With enough sponsorship, we can:

  • Start a a 501(c)(3) non-profit organization to hold ownership of Vale. 0
  • Buy the necessary computers to support more architectures.
  • Work on this full-time.
  • Make Vale into a production-ready language, and push it into the mainstream!

We have a strong track record, and during this quest we've discovered and implemented a lot of completely new techniques:

  • The Linear-Aliasing Model that lets us use linear types where we need speed, and generational references where we need the flexibility of shared mutability.
  • Region Borrowing, which makes it easier to write efficient code by composing shared mutability with the ability to temporarily freeze data.
  • Higher RAII, where the language adds logic safety by enforcing that we eventually perform a specific future operation.
  • Perfect Replayability makes debugging race conditions obsolete by recording all inputs and replaying execution exactly.

These have been successfully prototyped. With your sponsorship we can polish them, integrate them, and bring these techniques into the mainstream. 1

Our next steps are focused on making Vale more user-friendly by:

  1. Finalizing the compiler's error messages and improving compile speeds.
  2. Polishing interop with other languages.
  3. Growing the standard library and ecosystem!

We aim to combine and add to the benefits of our favorite languages:

We need your help to make this happen!

If you're impressed by our track record and believe in the direction we're heading, please consider sponsoring us:

If you have any questions, always feel free to reach out via email, twitter, discord, or the subreddit. Cheers!

0

Tentatively named the Vale Software Foundation.

1

Generational references, the linear-aliasing model, and higher RAII are all complete, and region borrowing, fearless FFI, and perfect replayability have been successfully prototyped. Be sure to check out the experimental version of the compiler!