Basic Types

This section describes Austral’s basic built in types.

The Unit Type

The Unit type is the simplest type: it has a single value, the constant nil.

let unit: Unit := nil;

This is the equivalent of C’s void: functions which don’t return anything useful can return nil:

function foo(): Unit is
    return nil;
end;

Booleans

The built in Bool type has two values: the constants true and false. if and while statements require boolean arguments, there are no implicit type conversions or “truthy” values in Austral.

let t: Bool := true;
let f: Bool := false;

if t then
    printLn("true!");
else
    printLn("false!");
end if;

The not, and, and or operators do what you expect. If a and b are expressions of type Bool, you can write:

not a
a and b
a or b
a and (not b)

Just as with arithmetic, there is no operator precedence for logical operators in Austral: any expression beyond one level has to be fully parenthesized:

-- Not valid:
a and b and c and d
-- Valid:
a and (b and (c and d))

Integers

The following are Austral’s built-in integer types:

Name Width Signedness
Nat8 8 bits Unsigned.
Nat16 16 bits. Unsigned.
Nat32 32 bits. Unsigned.
Nat64 64 bits. Unsigned.
Int8 8 bits. Signed.
Int16 16 bits. Signed.
Int32 32 bits. Signed.
Int64 64 bits. Signed.
Index Platform-dependent. Unsigned.

Nat types are unsigned, they are natural numbers: they start at zero. Int types are signed, they are integers: they can hold negative and positive values. The number is the width or size of the integer in bits.

The Index type is a special case: it’s the type of array indices, and equivalent to C’s size_t type.

The minimum and maximum values of each type are:

Name Minimum Maximum
Nat8 0 28-1 = 255
Nat16 0 216-1 = 65,535
Nat32 0 232-1 = 4,294,967,295
Nat64 0 264-1 = 18,446,744,073,709,551,615
Int8 -27 = -128 27-1 = 127
Int16 -215 = -32,768 215-1 = 32,767
Int32 -231 = -2,147,483,648 231-1 = 2,147,483,647
Int64 -263 = -9,223,372,036,854,775,808 263-1 = 9,223,372,036,854,775,807
Index 0 Platform-dependent.

The usual arithmetic operators are defined. If a and b are variables of the same integer type, you can write:

-a
a + b
a - b
a * b
a / d

Two notes:

  1. First, Austral has no arithmetic precedence: any arithmetic operation (or any binary operation, including Boolean and comparison operations) deeper than one level has to be fully parenthesized. The following will not parse:

    a - f(b - c/d) / e
    

    Instead:

    a - (f(b - (c/d)) / e)
    
  2. Second, Austral has no implicit type conversions. If you need to do arithmetic with types having different sizes and signedness, you have to convert them first.

Note on Overflow

Austral’s built-in arithmetic operators abort on overflow. If you want modular semantics, or you don’t want to pay the performance cost of overflow checking and can prove that overflow won’t happen, you can use the modular arithmetic methods:

modularAdd(a, b);
modularSubtract(a, b);
modularMultiply(a, b);
modularDivide(a, b);

Floating-Point Numbers

Austral has two built-in floating-point types: Float32 is equivalent to C’s float and Float64 is equivalent to C’s double. The arithmetic operators work on floats as well, but the usual restrictions apply: you can’t mix different float types, or floats and integers, in the same expression, you have to convert them.

Spans

String literals are spans of bytes:

let str: Span[Nat8, Static] := "Hello, world!";

The size of a span can be obtained with the built-in spanLength function, which takes a span and returns a value of type Index:

let size: Index := spanLength("Hello, world!");

Span elements can be accessed through the index operator:

let str: Span[Nat8, Static] := "Hello, world!";
printLn(str[0]);

If an index is out-of-bounds, the program aborts.