KL variable/attribute types

The type of a variable or record attribute determines what type/kind of information it can store/contain. For example, if a variable is of an integer type, then it can contain numbers with no fractional portion. Or if the type of the variable is a text type, then it can contain a piece of text.

The precise type of a variable or record attribute is actually specified as an expression to be evaluated. For a simple variable, the type can be specified as simply the name of a type, but actually variable/attribute types are expressions. Type expressions obey the syntax rules described in the Expressions section of this documentation.

In some cases, a variable/attribute type expression may include an arithmetic calculation, but only if the result of the calculation can be determined at compile-time (as opposed to runtime).

Examples

Equivalent source code in the C++ language is also provided, for the benefit of people with knowledge of C++, and also for comparison purposes. If you have no knowledge of C++, then you can ignore the C++ snippets within the following.

Functions

Storage Methods

An array can be stored using different methods. You can choose which method to use. The normal/default method is the "Dynamic" storage method, and it is the most powerful and least restrictive of the methods.

The purpose of using a different method in a particular situation is to improve the speed of the program, or to reduce the amount of memory used, or both (especially in situations where hundreds or thousands of record or attribute instances are being created).

These storage methods are described for arrays, but they are also applicable to text in the same manner.

* **Dynamic:**Suitable for frequent changes to the size/count of the array (resizes). If the array is large, this method usually provides better performance than the other methods.
* **Constant Size:**The size or count of the array (the number of items in the array) is a number that does not (and cannot) change. The desired size/count must be specified, and is the same for all instances of the array. For example, an array that always contains exactly 10 items (incapable of storing any more or less than 10 items). This is the fastest of all the methods, however its usefulness is limited.
* **Preallocated:**The memory for the array is preallocated to a maximum size when the array is created. The desired maximum size must be specified, and must be a constant number, and is the same for all instances of the array. The array cannot contain more items than will fit within the preallocated memory (attempting to insert another item when the array is full will cause an error). This method is faster than the "Dynamic" and "Consolidated" methods, however it may waste an excessive amount of memory if the maximum size is large while the amount of memory actually needed/used is much less.
* **Appended:**The memory for the array is appended to the record (contiguously). The array can be resized but only when there exists no more than 1 reference to the record or attributes within the record (because the record may need to be moved). Having multiple "Appended" attributes in the same record is supported (the second is appended after the first). Usually slower than "Preallocated" but consumes less memory. Usually faster and more efficient than "Consolidated".
* **Unchanging Size:**The size/count of the array must be specified for each record instance when it is created. The size/count can be different for different instances, but cannot be changed after a record is created. Faster and more efficient than the "Dynamic" and "Consolidated" methods, however its usefulness is limited by the inability to change the size after creation. There is little difference between the "Unchanging Size" and "Appended" methods, however "Unchanging Size" gives the compiler greater opportunities for optimization and it may achieve a speed improvement.
* **Consolidated:**All arrays using the Consolidated method within the same record instance will be stored within the same single chunk of memory. This can improve the performance of the program by decreasing the number of separate memory allocations required (fewer larger allocations is generally more efficient than many small allocations). Slower to change the size/count of the array (resize) than the "Dynamic" method. Faster to create and destroy many records than the "Dynamic" method. Usually uses less memory than the "Dynamic" method.

Internally, there may be a difference between resizing an array, and resizing the memory allocated for the array. Only the first type of resizing changes the count of the array (the number of items in the array). Either type of resizing may or may not require the simultaneous use of the other type of resizing, depending on the situation.

Regarding the "Appended" method, when there exists more than 1 reference to the record, both types of resizing are prohibited and must generate an error if attempted. If resizing the array could succeed without resizing the allocated memory this time in this case but not in all cases, it is prohibited and will generate an error. This behavior improves reliability and gives a consistent response, and makes it easier to detect certain programming mistakes.

Specifying an Array Type

When specifying an array type, you are required to specify some other type. Each item in the array will be this other type that you specify. For example, the following means an array of uint8 (unsigned 8-bit integer) variables:

```
TArray[uint8]

```

Different storage methods can be used. If no storage method is specified (as in the above example), then the "Dynamic" method is assumed by default. See the "Storage Methods" section for a description. You can optionally use one of the functions that selects a different storage method. For example, the following means an array of uint8 variables, using the "Consolidated" storage method:

```
Consolidated[TArray[uint8]]

```

When using the "Constant Size" storage method, it is necessary to specify the count/number of items in the array. This count/number must be a constant number or an expression resulting in a constant number, that can be determined at compile-time (as opposed to runtime). For example, the following means an array of 100 uint variables.

```
ConstantSize[TArray[uint], 100]

```

Using the "Preallocated" storage method is similar to "Constant Size", except that the count/number of items is the maximum count of the array, meaning the maximum number of items that the array is capable of storing. For example, the following means an array capable of storing up to 100 uint variables:

```
Preallocated[TArray[uint], 100]

```

To make an array of text attributes:

```
TArray[TText]

```

It is possible to make an array of arrays. For the item type of the array, simply specify another array. For example:

```
TArray[TArray[uint]]

```

Specifying a Text Type

To specify that a variable be capable of storing a piece/chunk of text, it is as simple as this:

```
TText

```

A Unicode text format can optionally be specified:

```
TText[UTF16BE]

```

The default text format is usually UTF8. The following formats are supported:

 UTF16BE
 UTF16LE
 UTF16HE

Different storage methods can be used. If no storage method is specified, then the "Dynamic" method is assumed by default. See the "Storage Methods" section for a description. You can optionally use one of the functions that selects a different storage method. For example, the following means a text variable using the UTF16BE text format and the "Consolidated" storage method:

```
Consolidated[TText[UTF16BE]]

```

When using the "Preallocated" storage method, it is necessary to specify the maximum size (in bytes) of the text. This size must be a constant number or an expression resulting in a constant number, that can be determined at compile-time (as opposed to runtime). For example, the following means a text variable using the default text format and the "Preallocated" storage method with a maximum size of 100 bytes:

```
Preallocated[TText, 100]

```

The following means a text variable using the UTF16BE text format and the "Preallocated" storage method with a maximum size of 100 bytes:

```
Preallocated[TText[UTF16BE], 100]

```

When specifying sizes for storage methods for text, note that the size is the number of bytes, NOT the number of characters. It is necessary to specify it in bytes because the size of a character varies depending on the particular character (regardless of whether UTF8 or UTF16 is used).

Any Type

A reference to any type can be defined:

```
Ref[TAny]

```

Given such a reference, it is possible to check at runtime whether it is actually a reference to a specified type of variable/attribute, and safely typecast the reference to a reference of that type, and then the value can be accessed (read or modified). This is possible with any type, including primitive types (such as uint32) and records.

A "TAny" reference cannot be forcibly typecast to a specified type, because that could result in memory corruption and crashes. The typecast operation will only succeed if the type of the variable/attribute matches (or is directly compatible with) the specified type. If not, then an error is generated.

The "TAny" type cannot be used to circumvent, bypass, or cancel read-only protection. If a type is read-only, it cannot be typecast to a non-read-only "TAny". And a read-only "TAny" cannot be typecast to a non-read-only specific type.

Given a "TAny" reference, it is also possible to check at runtime whether it is a reference to any type of record that contains an attribute of the specified name and type, and to get a reference to that attribute. This allows the creation of a combined or compound "object" that contains various different components, with components being optional or interchangeable. For example, the functionality/behavior of the "object" may be determined by the particular combination of components that it contains.

Pointers

In a type expression, the "Ptr" operator is used to make a pointer to a type. Pointers are much the same concept as references, except lower level and unchecked/unverified/uncontrolled. The "Ptr" operator has 1 operand the same as the "Ref" operator, indicating what type of data the pointer will be pointing to (the type of the target).

Pointers are for low-level tasks and system implementation, not for application programs. Normal application programs cannot use pointers, they will be denied access if they try to.

References have automatic memory management, pointers do not. The automatic memory management system operates blithely unaware of pointers (the existence of a pointer to a record does not prevent destruction/deletion of that record).

It is possible for a pointer to be invalid (pointing to a non-existent or deleted record/object/variable, or pointing to the wrong type of data, or pointing to a random location). If a pointer is not used correctly and carefully, it can cause a crash, obscure malfunction, or security hole.

Pointers are always nullable. Attempting to access the target of a null pointer can cause a crash (whereas for a reference, it would cause an error).

A pointer is actually a 32-bit or 64-bit unsigned integer RAM address (the ordinal of a byte in RAM).