The Vale Programming Language

Here is a basic Spaceship struct, with a couple members.

We can construct it using its constructor function, which has the same name and was automatically generated.

All structs are mutable by default, more on that in the Mutability section below.

Vale

struct Spaceship {
name str;
numWings int;
}


fn main() export {
ship = Spaceship("Serenity", 2);
println(ship.name);
}
stdout
Serenity

Constructors

We can specify a custom constructor for our struct.

We just need to give it the same name as the struct. 0

Vale

struct Spaceship {
name str;
numWings int;
}

fn Spaceship() Spaceship {
Spaceship("Serenity", 2)
}


fn main() export {
ship = Spaceship();
println(ship.name);
}

Notes
0

Inside the constructor, we must call either another constructor or constructor<T>.

Ownership

Every mutable struct has exactly one owning reference at any given time. 1 2

When we create a mutable struct, we get the owning reference to it. When the owning reference disappears, the struct is automatically deallocated (via drop, explained below).

Vale
fn main() export {
ship = Spaceship("Serenity", 2);
// ship is an owning reference

println(ship.name);

// implicitly drops ship
}

1

There are other kinds of references (constraint, borrow, weak), References explains more.

2

Ownership is also found in C++ (unique_ptr), Rust, and Cyclone.

C also has "conceptual" ownership, in that we must track ownership without the language's help, to know when to free a struct.

Vale's ownership has the flexibility of C++'s unique_ptr without the mutability and aliasing restrictions of Rust and Cyclone, see References to learn how.

Lending

Every mutable struct has exactly one owning reference pointing to it.

We can make another reference to a struct with the & symbol. It will be a non-owning reference. 3 This is called a lend.

In this example, the type of owningRef is Spaceship, and the type of nonOwningRef is &Spaceship.

Vale
fn main() export {
owningRef = Spaceship("Serenity", 2);
nonOwningRef = &owningRef;
}

3

More specifically, a constraint reference, see References.

Moving

A local can give up the owning reference. Afterwards, that local is gone, and we cannot use it. This is called a move.

This is used to influence when the struct is dropped, to keep it alive for longer or destroy it sooner.

In this example, the a local is giving up the owning reference, and we're putting it into the b local.

Vale
fn main() export {
a = Spaceship("Serenity", 2); 4
b = a; // Move the ship from a to b.
// b now owns the Spaceship.
// Can't use a now.
}

It works the same way when passing an owning reference to a function.

Vale
fn foo(b Spaceship) {
println(b.name);
}

fn main() export {
a = Spaceship("Serenity", 2);
// Move the Spaceship from a
// into foo's b
foo(a);
// Can't use a now.
}
4

Rather than thinking "a is an owning reference", think instead that a is a local that currently contains an owning reference.

On the next line, a is destroyed, but the owning reference that it contained still lives on (inside b).

Drop

When an owning reference disappears, the struct is automatically deallocated. Vale does this by inserting a call to drop. 5

The drop function is automatically generated for each struct and interface.

Here we can see where the implicit call to drop is.

drop is called when the owning reference goes away, which in this case is ship.

Vale
fn main() export {
ship = Spaceship("Serenity", 2);
// ship is an owning reference

println(ship.name);

// implicit ship^.drop().
}


// Implicit:
// fn drop(s Spaceship) {
// destruct s; 6 // frees
// }

We can specify our own drop function. A custom drop could be used to:

  • Remove this object from an observers list.
  • Commit a transaction.
  • Inform other objects of this object's destruction.

Rule of thumb: if something must happen at some point in the future, put it in a drop function. Vale will make sure that it's not forgotten. 7

5

Drop functions also appear in C++ ("destructors") and Rust. Vale's drop functions are like those but more flexible: they can return values and even take extra parameters. In those cases, they must be called manually. See The Next Steps for Single Ownership and RAII for more!

6

The destruct keyword is syntactic sugar for a "move destructure" pattern, see Pattern Matching for more.

7

This is an incredibly powerful pattern, see The Next Steps for Single Ownership and RAII for more.

Mutability

By default, structs are mutable. We can make immutable structs with the imm keyword.

After construction, an immutable struct cannot be changed at all.

Because of that, we can have multiple owning references to it, like Java or Python. 8

Vale also automatically derives the functions println, str, hash, ==, and more.

Immutable structs cannot have drop functions. 9

Vale

struct Spaceship imm {
name str;
numWings int;
}


fn main() export {
ship = Spaceship("Serenity", 2);
println(ship.numWings);
}
stdout
Spaceship("Serenity", 2)

8

Small immutable structs (32b or less) are copied and passed by-value. Larger objects use SNRC (strategic nonatomic reference counting) to free themselves.

9

See The Next Steps for Single Ownership and RAII for the reasoning behind this.

Planned Features

These are planned features for Vale. See the roadmap for plans!

Shortcalling Constructors

We normally call a function by name, such as the Spaceship("Serenity", 2) above. However, if the code is expecting a certain type, it can automatically call the constructor of the expected type.

Vale
// Using above Spaceship struct

fn main() export {
// These statements are equivalent:
x Spaceship = Spaceship("Raza", 2);
x Spaceship = ("Raza", 2);
}

This saves a lot of typing when calling functions.

Vale
// Using above Spaceship struct

fn foo(s Spaceship) { ... }

fn main() export {
// These statements are equivalent:
foo(Spaceship("Raza", 2));
foo(("Raza", 2));
}

Next: References