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.
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
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.
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
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.]
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);
}
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.
The default behaviour can be altered using pragmas:
(deprecated) doesn't have effects on translation, but throws a warning when the function is called.
(unsafe) allows for unsafe operations inside the function (manual memory management: raw pointer operations like reference and dereference). Also, unsafe functions can only be called from other unsafe functions (the exception are overloads). You can often cheese the dereference by doing [0], but why? Why?
(inline) replaces all function calls with the function themselves (see Inline expansion), instead of calling it. This is significantly more memory extensive as a new instance of the function is basically generated for each call, but it is faster: there is no stack shenanigans or jumping. Really useful for very basic functions which you might want to act more like macros than actual functions. It is to be noted that inline functions cannot be virtual.
(volatile) disables compiler optimizations for the function. For example, the compiler might want to do optimizations which don't produce any results for the purpose of the function but have side effects on memory: this might be undesired behaviour for low level programming.
(interrupt) acts like an interrupt handler and returns with iret instead of ret, along with other fixes that avoid what OSDev.org likes to call "black magic".
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 )