Functions


Functions are reusable pieces of code. They have an identifier, a return type, a set of parameters (variables that can get passed inside the function) and a body of executable code. They can also be methods of a parent data type.

Syntax


fun pragmasopt type name <generics>opt (parameters) body

where type is the return type of the function. If type is missing, v is assumed. For methods, the syntax is pretty understandable:

fun pragmasopt type parent.name <generics>opt (parameters) body

For methods, all of the parent type's generics are inherited as well.

virt pragmasopt type parent.name <generics>opt (parameters) body

Examples (signature only)


fun Main (u64 argc, u8** argv)

The entry point of every [name] program!

fun T Max (T x, T y)

A function, Max, which uses generics. All the three occurrences of T are replaced with the type inferred from the parameters.

fun T LinkedList.Back()

A method, Back, which inherits the generic T from the type LinkedList we've seen before.

Overloads


Operators of [name] can be overloaded to be compatible with user-defined types. Most operators [which?] can be overloaded like this:

fun Vector2 +operator (Vector2 x, Vector2 y)

Operators are to the user's discretion, as long as at least one of them isn't already defined by the language.

fun Vector2 *operator (Vector2 vec, f64 m)

fun Vector2 /operator (Vector2 vec, T m)

fun Vector2 +operator (i64 a, i64 b) // Not allowed!

There are also two special overloads for two important language features, with a pre-defined signature which must be respected: [This whole thing might be revised as it might be bad to do it with generics (for every single custom type the user makes, a new function (although short) is added, and it's THE LITERAL SAME as well, if we make len in bytes instead of units of T which i see no reason not to). We MIGHT have to make it return an u64, because we dont have void pointers, which is a thing i REALLY REALLY would like to avoid. even if its just for the stdlib.]

New

fun T* new<T> (u64 len)

Its expected behaviour is to allocate an area in memory enough for len objects of type T, and return a pointer to the start of said area.

[This section is obviously not verified as there is no implementation, also it might go outside the purpose of the docs to include it] The standard implementation (as for System is this:

fun T* new<T> (u64 len) {
return MAlloc(len * sizeof T);
}

Destroy

fun destroy<T> (T* ptr, u64 len)

Basically the exact reverse thing of new. It's supposed to free the area of memory starting at ptr for len objects of type T.

Behaviour


The default behaviour can be altered using pragmas:

Lambda functions


Lambda functions are inline functions that can be either ran on the spot or stored in a variable. They come in two shapes:

The first one runs a block of code:

(*arguments*) => {*body*}

The second one, instead, evaluates an expression:

(*arguments*) => (*expression*)

As such, this second form is actually shorthand for this:

(*arguments*) => { return (*expression*); }

For example, a function that, when applied to an integer list, only leaves items whose value is positive, could look like this:

(i64 x) => ( x > 0 )