by Dhruv Rajvanshi
A systems level programming language that compiles to LLVM


I think there's a lot of room for experimentation in the systems languages space. This language aims to sit between C and Rust, hopefully leaning towards Rust in terms of safety and towards C in terms of simplicity.

Takes inspiration from following languages:

  • C++
  • Rust
  • Haskell

And a host of experimental and niche languages like Zig, Lobster, C3, and Odin.

Here's a short list of things which I think Hades aims to improve over its influences


Error messages: C++, due to the nature of templates, is very hard to produce good error messages for. C++ templates bodies are checked per use site. i.e. Template bodies don't show type errors. In fact it depends on this behaviour for overload resolution (SFINAE). Hades has constrained generics in the form of interfaces (similar to Rust/Haskell in this regard).


Ergonomics. I think it's worth sacrifising some performance to improve ergonomics. Hades doesn't aim to provide the memory and aliasing safety guarantees that Rust provides. If we can enforce some of that with runtime checks while being more ergonomic than lifetime annotations, that would be nice.

// A struct has a packed layout like C
struct Pair[A, B] {
  val first: A;
  val second: B;

def main(): Void {
  if true {
    val pair = Pair(1, b"text"); // Type arguments to Pair are inferred
    print_pair_second(pair); // function arguments are passed as value (a copy of pair is sent to print_pair
    val pair_ptr = &pair; // you can take address of local variables and pass them as pointers
    pair.print_second(); // this is an extension function call

def print_pair_second[T](pair: Pair[T, *Byte]): Void {

// extension methods can be defined for any type including types from
// different libraries

// some/nested_module.hds
extension PairExtensions[T] for Pair[T, *Byte] {
    // `*this` means this extension takes its receiver by pointer
    // Use `*mut this` to treat receiver pointer as mutable
    // and `this` to take it as value
    def print_second(*this): Void {
      c.puts(*this.second); // this.second gives pointer to the second field of the struct. Dereference it using prefix *

// in another file

// Importing a module brings extensions exported by that module
// into scope. This isn't transitive i.e. extensions imported by
// nested_module would not be available here. You have to separately
// import those.
import some.nested_module as nested_module;

def main(): Void {
    val x = Pair(true, b"x");
    // because extension method is declared as *this,
    // we have to take pointer to x to pass it to the method.
Information updated 02/22/21
View Comments