The Vale Programming Language

Welcome to Vale! Vale is a fast, safe, and simple programming language. This introduction will show how to write a program in Vale.

This reference assumes that you are familiar with basic programming concepts, and at least one other imperative language (like C, Java, Python, etc).

Vale is still "in alpha", which means it's a preview; you can write programs in it, but some of the features that make it easy aren't yet available.

Hello world!

Run the code to the right 0 to see it produce the output in the gray box.

Vale

fn main() export {
println("Hello world!");
}
stdout
Hello world!

Locals

We can make a local with the = symbol.

By default, a local is "final". A final local cannot be changed after it's made.

Vale

fn main() export {
x = "world!";
println("Hello " + x);
}
stdout
Hello world!

We can make a varying local with the ! symbol1, and change it later with the mut keyword. 2

Vale

fn main() export {
x! = "world!";
mut x = "Antarctica!";
println("Hello " + x);
}
stdout
Hello Antarctica!

Notes
0

You can put the code into e.g. hello.vale and invoke the compiler with python3 valec.py hello.vale. (After 0.1, it will simply be vale build hello.vale.)

That will produce an executable named hello.exe (on Windows) or hello (on Mac or Linux).

1

Vale's x and x! are like Java's final String x and String x.

2

mut x does not declare a local named x. It changes an existing local's value.

Static Typing & Inference

Vale is a statically typed language, which means the type of every local and member is known at compile-time.

In this example, a is a str. We can even specify it after the local's name.

...though we usually leave it out, because Vale uses type inference to figure out the type of a for us.

Vale

fn main() export {
a str = "world!";
println("Hello " + a);
}
stdout
Hello world!

Functions

Here we have a simple function that returns the argument plus two.

Vale

fn add2(x int) int {
ret x + 2;
}

fn main() export {
println("Half-Life " + add2(1));
}
stdout
Half-Life 3

If we use the infer-ret keyword, Vale will automatically figure out the return type of the function. 3

Vale

fn add2(x int) infer-ret {
ret x + 2;
}

If a block has only one line, we can leave off the ret and the semicolon.

Vale

fn add2(x int) int { x + 2 }

We can also make a lambda, a function inside another one. 4

Vale

fn main() export {
add2 = (x int){ x + 2 };
println("Half-Life " + add2(1));
}

We can leave the type off of a lambda's parameter, to make it shorter. 5

Vale

fn main() export {
add2 = (x){ x + 2 };
println("Half-Life " + add2(1));
}

We can shorten lambdas with the almighty underscore, which declares and immediately uses an implicit parameter.

Vale

fn main() export {
add2 = { _ + 2 };
println("Half-Life " + add2(1));
}

In Vale, functions and methods are the same thing, 6 so these two calls are exactly equivalent. 7

Vale
fn main() export {
s1 = "Hail Hydra!".split(" ");
s2 = split("Hail Hydra!", " ");
}

3

infer-ret should be used sparingly, as it increases compile times.

4

See the Tuples section below for an example of how lambdas are super useful.

5

Taking out the parameter's type makes this a "generic" lambda, see generics for more.

6

This is known as Universal Function Call Syntax. This makes method chaining nicer, for example

a.f(3).g(true).h("Yendor")

as opposed to

h(g(f(a, 3), true), "Yendor").

7

Include vstl/list.vale for the split function.

Tuples

A tuple is a simple struct, whose members are named 0, 1, 2, etc.

We can make a tuple in Vale with square brackets (like [5, true, "V"]), and can access them with a dot ,like arr.0.

Vale

fn main() export {
arr = [5, true, "V"];
println("Babylon " + arr.0);
}
stdout
Babylon 5

Arrays

We can only make a tuple if we know the number of elements at compile-time. If we don't know, then we must use an array.

We can make an array with the Array function.

Vale

fn main() export {
n = stdinReadInt(); 8
arr = MakeArray(n, {_ * 5}); 9
arr.each({ println(_); });
}
stdin
3
stdout
0 5 10

8

stdinReadInt reads an integer from the user's keyboard. In this example, the user is entering 3.

stdinReadInt is experimental, expect the stdin/stdout API to change soon.

9

The type of arr here is Array<int>.

Lists

Arrays can't be resized at run-time. Lists can!

We can make one with the List function. 10

Vale
fn main() export {
l = List<int>();
l.add(1);
l.add(3);
l.add(7);
println(l.get(2));
}
stdout
7

10

Vale's lists are like C#'s List, Java's ArrayList or C++'s vector; it's backed by an array, not a linked list.

Loops

We can loop with the each statement: 11

Vale

fn main() export {
arr = ["Storm", "Earth", "Fire"];
arr.each((x){ println(x); });
}
stdout
Storm Earth Fire

We can also get the index of the current iteration with the eachI statement.

Vale

fn main() export {
arr = ["Storm", "Earth", "Fire"];
arr.eachI((i, x){
println(i + ": " + x);
}
)
;
}
stdout
0: Storm 1: Earth 2: Fire

11

The each statement is syntactic sugar for the each method:

each arr (x){ println(x); }

is equivalent to

arr.each((x){ println(x); })

Planned Features

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

Variants

We'll be able to produce a variant, which is something that can be one of two types (or more).

To make a variant, use an if-statement, and return a different type from the then branch than the else branch.

Here, a is a variant, either a string or an integer. a's type is (str|int).

fn main() export { a = if (true) { "hello" } else { 42 }; }

Later, we can use the match statement to determine if a actually contains a str or an int.

Indexing Structs or Tuples for Variants

We'll be able to access a arbitrary fields of tuples or structs with square brackets, like arr[1 + 1], which would produce a variant. 12

fn main() export { arr = [5, true, "V"]; println("Saturn " + arr[1 + 1]); }
stdout
Saturn V

12

Specifically, indexing this tuple gives a variant (int|bool|str), with a + function that calls the appropriate actual + depending on the run-time type (int vs bool vs str).

Next: Structs