-
Notifications
You must be signed in to change notification settings - Fork 14
Description
Type system
A type system is planned for one of the upcoming versions of Mindcode. It's a large task and will certainly be implemented over several iterations. This issue serves for clarification and discussion on the concept.
Simple types
Simple types are based upon processor variables. The basic (topmost) type is var, exactly corresponding to a processor variable. Other types are specific subsets of var. There will be a hierarchical structure of types, with widening/narrowing conversions. Possible structure:
graph TD;
var --> float;
float --> float_range;
float_range --> color;
float_range --> int;
int --> int_range;
int_range --> int+;
int+ --> bool;
var --> enum;
var --> mlog_object;
mlog_object--> string;
mlog_object--> building;
mlog_object--> mlog_content;
mlog_object--> unit;
mlog_object--> property;
mlog_object--> sound;
mlog_content--> item;
mlog_content--> liquid;
mlog_content--> block;
mlog_content--> unit_t;
int+ is a non-negative integer. The range types hold int/float values from a specific range.
Type conversion function will be used. It is important to distinguish a conversion which ensures the input value is conform to the target type, and a conversion which assumes the input already conforms and only gives the information to the compiler. E.g. block.sensor(property) returns a type of var, but we might know the property only takes on values that ensure the result will always be int+.
It might or might not be possible to explicitly declare float and int ranges. (What about the syntax? The best idea so far is int[0 ... 10] x declares a variable x of the integer range 0 .. 9, but it is easy to confuse with arrays.)
Types will provide more information to the compiler/optimizer, type checking and enforcing will make programs safer.
Simple types can be stored in Mindcode variable and passed freely to functions.
There might be an implicit conversion to/from ID when storing content types in a memory call or memory bank.
Enums
Stored as integer values, starting at 0. Support for:
- list iteration loops,
- optimized case switching,
- internal array indexing,
- previous() and next(),
- name() - provides the name of the enum (maybe different from literal).
Implicit conversion to int when storing in memory cell/unit flag. Explicit conversion from int, to make sure the resulting value is always within bounds.
Nothing too fancy, mostly just syntactic sugar.
Records/structures
Record will be a user-defined type consisting of members. Each member of a record/structure will be stored in a processor variable.
- Nested records
- Fixed-size arrays
- Single inheritance
- Operator overloading?
Enables multivalued returns from functions.
Possibly a Vector record consisting of x, y members, with arithmetic operations defined on them.
Unions (optional)
Like C unions. Possible use cases:
- Overlaying an array over a record, bringing up a possibility to access record elements as arrays. Plus variations of his topic.
- Creating a record that can hold two different (sub)structures exclusively, while not wasting processor variables on them. Possible performance savings when passing such records in/out of functions.
Function calls
Records are copied to function variables when passed as both in or out parameters.
Function-specific counter arrays will be created for records containing arrays. This also provides a mechanism to pass fixed-sized arrays to non-inline functions. It's a bit cumbersome to have to enclose such an array in a record, so maybe a specific syntax for declaring types corresponding to fixed-size arrays might be created.
A warning might be generated when the size of the record being passed as an argument is higher than some threshold.
Objects
Objects would be based on records with these extensions:
- Support for variable-sized member arrays
- Methods
- Object references
- Polymorphism through interfaces
- Inheritance?
Variable-sized member arrays
As efficient dynamic allocation is impossible in mlog, all objects will have to be created statically, either as local or global variables. All variable-sized arrays will have to be set to a specific size in constructor. Jump tables will be generated individually for all arrays within the object.
Since the allocation is static, the compiler will know the exact composition of each object instance.
Methods
Methods will be compiled separately for each object instance, as member variables will be accessed directly in the method. Special handling might be implemented for fixed-sizes members:
- Methods accessing only fixed-sized members could be compiled just once, taking the members as implicit parameters.
- Arrays of objects, if supported at all, might create shared methods for all elements of the array.
Function calls
Fixed-sized objects might be passed into functions in the same way as records. Optional.
Object references
Object references would be stored in mlog variables, allowing objects to be assigned to Mindcode variables and/or passed into functions.
Method calls would have to be realized effectively through function pointers. The object reference would be an instruction address of a vtable (a jump table where every element would jump to the beginning of the corresponding method of the object). Therefore, the object reference would be a single value. Given that the reference is a number, it can be stored in external memory/stack.
Function call mechanism:
- setup function parameters and return value,
- jump to an offset from the object pointer corresponding to the method being called (single
op add @counter objectReference methodIndexinstruction), - retrieve function output variables.
Consequences:
- Different object instances would share function parameters (so that the caller can set up all parameters the same way). Local variables could be different.
- Calling the same method on an object reference would be effectively a recursive call (and could be supported as such), due at least to shared function parameters.
Interfaces
Function pointers would be implemented through interfaces. An interface with a single method (a lambda interface) would effectively serve as a function pointer type. The vtable would be eliminated in this case and the pointer would point directly to the function.
Again, calling the same interface method through a function pointer would be effectively a recursive call.
Polymorphism
Polymorphism would be primarily provided through interfaces. A library could define an interface and accept implementations.
Multiple inheritance (including multiple inheritance from interfaces) could be problematic, as this would complicate the vtable. It's not impossible, though.
Support for abstract methods is possible, however the benefit for Mindcode projects is not immediately apparent.
Object inheritance
Principally possible, including the ability to assign a child instance to a reference of a parent type.
Remote objects
A reference could point to a remote object. Mechanism would be similar to current remote method calls: the remote @counter would be set according the the vtable of the object. The validity of the vtable depends on the method structure of the object, must be compatible in both processors (i.e. compiled from the same source), similarly to current remote calls.