Custom data types


[name] allows for user definition of custom data types. They are typically a set of attributes (variables internal to the data type and accessible from the outside with a system of visibility rings), and a set of methods (functions exclusive to the data type).

Parenthesis on generics


We can give a "name" to an unknown data type, just like we can call the variable "x" in equations and such. This allows us to have a data type which is variable, but consistent over the whole context of custom type, function or even a type's methods. In a type, the generics have to be specified in order for the actual memory structure of the type to adapt. In functions, the generics can be inferred automatically from the arguments (a function with only a generic as return type may not exist, except in the context of constructors/destructors), and a new "version" of the function is generated by the compiler to work with the required type. Only types that are actually used with a function have a version of the function generated for them.

Syntax


In [name], type definition is done as such:

type pragmasoptname <generics>opt (attributes)

type pragmas opt name <generics>opt (attributes) : source

Or to inherit properties from an existing custom data type:

type pragmasopt name <generics>opt (attributes) : source

This syntax can also be used to create aliases of other data types, in a similar fashion to C's typedef (note: for normal inheritance, only another custom data type's name is allowed):

type name : source

The syntax for methods is explained in the Functions Page.

Attributes of custom types can make use of the special keywords priv and rdonly to change their visibility.

Examples


type Vector2 (f64x, f64y);

Defining a type, Vector2, with attributes f64 x and f64 y

type Vector3 (f64 z) : Vector2;

Defining a type, Vector3, inheriting Vector2 and adding attribute f64 z

type LinkedList (T val, priv LinkedList* prev, priv LinkedList* next);

Defining a type, LinkedList, which uses generic T for the user to be able to choose the type of the value.

type CString : u8*

Creating an alias, CString, for type u8*

Behaviour


The default behaviour of custom data types is object-like: the developer should not have to care about its internal structure, as order and alignment are optimized by the compiler. Also, custom data types have a base structure common to all objects containing a virtual dispatch table (containing the `u64 Hash()` function), an identifier unique to the type (for use with `instanceof`), both unaccessible as normal attributes; Additional attributes or methods are to be added in the future, but again, the developer shouldn't rely on this structure anyway.

The default behaviour can be altered using pragmas:

Usage


Custom data type variables are inherently pointers of said variable. Though, note that the * operator can NOT be used on it. The reason for this is that i don't see any reason why one would want it. So:

var Vector2 vect; // actually a pointer to a Vector2 in memory

As mentioned above, objects aren't intialized automatically. To initialize an object, there are two common ways. The first requires a standard library or a user implementation of new:

vect = new Vector2(); // fill according to constructors

It is worth noting that an empty constructor isn't automatically created for a data type. The compiler will throw an error in that case. The standard library implementation of new allocates area in the heap, then calls the constructor. The second way doesn't allocate anything, and it just calls the constructor:

vect = (Vector2)whatever; // casting to pointer requires unsafe flag

vect.Vector2();

It is worth noting that the behaviour of = for objects is to copy the pointer.

vectA = vectB; // vectA is now an alias for vectB, in the same area of memory

vectA.Vector2(vectB); // vectA is now a copy of vectB, in its own area of memory

Be aware while working with raw pointers like this, as there is no garbage collection going on. As such, this requires the unsfae flag.

Obviously, when creating an object of a type that requires generics, telling the compiler the type is mandatory, both in the constructor and in the definition.

var LinkedList<i64> myIntList = new LinkedList<i64>();

Constructors/destructors


Methods are mostly up to the user, but there are two method definitions which have a specific syntax which must be followed:

Constructors

fun pragmasopt dataTypeName(attributes) {}

Allowed pragmas for constructors are: (volatile), (inline), (deprecated)

Destructors

fun pragmasopt ~ dataTypeName() {}

Allowed pragmas for the destructors are: (volatile), (inline)

Other built in methods

Every custom data type also comes with default overrideable virtual methods:

Hash

Returns an object's unique hash. Should avoid collision. Used in match statements, hashmaps and == by default. Default definition is as such:

fun u64 Hash () => 0;