Reflect

Functions

⚠️ TODO

Example:

function square(x) => x ^^ 2;

function square2(x: int) : int
{
	return x ^^ 2;
}

assert(square(3) == 9);
assert(square2(3) == 9);

Normal Functions

⚠️ TODO

Parameter Type Inference

Function parameters can have an explicit type, or can restrict the allowed argument types using one of the following means.

Typed Parameters

function foo(x: int) => 2 * x;

Generic Typed Parameters

struct S(T: type) { var value: T; }
function foo(x: S) => 2 * x.value;

Constrained Parameters

Example:

function foo(x: isIntegral) => 2 * x;

Partially Typed Function Parameters

Example:

function foo(x: function(x: int)) => x(2);

Unconstrained Parameters

Example:

function foo(x) => 2 * x;

Type Aliases

The type of a particular parameter can be assigned to a type alias that then can be used to define or restrict other parameter types. The compiler will attempt to pattern match this type alias to determine its value from all eligible parameters that use it to define their type.

Example:

function sum(a: isIntegral alias T, b: T[])
{
	var result = a;
	for (val; b) result += val;
	return result;
}

Parameter Type Aliases

In some situations, it is desirable to be able to force several parameters to (partially) share the same type. This is supported by adding a type alias to one of the parameters, which can then be used to constrain the types of the other parameters.

function fun(a: isIntegral alias T, b: T, c: T[]);

Parameter Reverse Overload resolution

When passing a function overload, a generic function or an object with a generic operator[call] to a parameter that has a function or partial function type, the compiler will perform a reverse-overload resolution on the passed expression to select a callable that matches the parameter type.

Example:

function foo(x: int) => x * 2;
function foo(x: float) => x * 4.0f;

function bar(fun: function(int), x: int) => fun(x);

assert(bar(foo, 10) == 20);

Return Type Inference

The return type of a function that does not explicitly define one will be inferred by computing the common type of all values passed as return values to a return statement within the function body. If no value is returned, the return type will be inferred as void. It is illegal to declare a function with no body and no explicit return type.

Variadic Parameters

⚠️ TODO

Tuple Based

function printFormatted(format: string, arguments...) {}
printFormatted("The %s is %s", "answer", 42);

Array Slice Based

function addItems(items...: int[]) {}
addItems(1, 2, 3);
addItems([1, 2, 3]);
addItems(items: [1, 2, 3]);

Static Array Based

function setCoordinates(coordinates...: int[3]) {}
setCoordinates(1, 2, 3);
setCoordinates([1, 2, 3]);

C-Style

@extern("C") function printf(format: const(char)*, ...): int;

Universal Function-Call Syntax

When calling a normal function, instead of writing all arguments in parenthesis, the first argument may instead be written in front of the function, resembling a method call. Conversely, a method can also be called by referencing the method name as a regular function outside of the aggregate and passing the aggregate as the first argument.

This alternative syntax naturally allows to write "extension functions", as well as functional call chains.

Example:

struct Point {
	var x: int;
	var y: int;

	method clear() { x = 0; y = 0; }
}

function moveRight(ref point: Point, amount: int)
{
	point.x += amount;
}

var pt = Point(10, 10);

// regular function call
moveRight(5);
assert(pt == Point(15, 10));

// regular method call
pt.clear();

// function called as method
pt.moveRight(5);
assert(pt == Point(15, 10));

// method called as function
clear(pt);

Inline Functions

Functions can be defined anonymously as inline-expressions. Both the regular syntax using an explicit function body and the lambda syntax using => are supported.

Example:

var fun1 = (x, y) { return x + y; }
var fun2 = (x, y) => x + y;
var fun3 = x => 2 * x;

assert(fun1(2, 3) == 5);
assert(fun2(2, 3) == 5);
assert(fun3(3) == 6);

Closures

Prepending an inline function expression with a list of variable names accessible in the current scope creates a closure that captures or references the listed variables. Internally, a closure is represented as an inline defined struct that stores the captured variables as fields and has a "call" operator that contains the function body of the closure.

Example:

var local = 42;
var fun1 = [local] (x) { return x + local; }
var fun2 = [local] (x) => x + local;
var fun3 = [ref local] (x) => local += x;

assert(fun1(10) == 52);
assert(fun2(10) == 52);

fun3(2);
assert(local == 44);