-
-
Notifications
You must be signed in to change notification settings - Fork 23
Onyx Type Info
Onyx allows your program to introspect information about the types in your program at runtime. This information includes:
- Structure member names and types
- Enum values and names
- Procedure types
- Much more
Onyx is not alone in having this feature; many scripting and partially compiled programming languages support this idea to some extent. Unlike some other languages where this information is hard to query and understand, Onyx tries its best to make the information very understandable and usable.
If you have never programmed in a language that has this feature (or have never used it), you might be wondering why you might want it. In short, having type information available to allows you automate many things that would have been tedious code duplication in other language. For example,
- Serializing/Deseralizing a value in JSON, CSV, or a binary format
- Dynamically building objects in your program (decoupling code)
In Onyx, all types are both compile-time and runtime values. This means you can use any type in an expression, and it will have the type type_expr
. This is implemented by assigning a 32-bit integer value to every type in your program, and then opaquely using that value inplace of the type.
This simple example shows using types as runtime values.
main :: () {
// some_type is variable of type 'type_expr', with value 'i32'.
some_type: type_expr = i32;
// Because some_type is like any other variable, == can be
// used to check equality between types.
if some_type == f32 {
println("some_type is a float!");
}
if some_type == i32 {
println("some_type is an integer!");
}
}
One rather obvious limitation is that variables cannot be created with runtime known types or casted using runtime known types. The following will not compile in Onyx.
main :: () {
some_type := i32;
// some_type is a runtime value and therefore cannot be used here.
a: some_type;
}
As stated above, type_expr
s are nothing more than 32-bit integers under-the-hood. To get useful information about types, you have to use the core.runtime.info
package. Here is a simple example that prints all the members in a structure.
// This procedure takes a runtime known type
print_members :: (T: type_expr) {
// This package provides helper procedures to access
// and under type information.
use core.runtime.info;
// get_type_info() takes a type, and return a ^Type_Info, which
// contains the information about the type. Here, it is immediately
// casted to a ^Type_Info_Struct, so that the '.members' field
// below can be accessed.
info := cast(^Type_Info_Struct) get_type_info(T);
// If this type is not of kind 'Struct', meaning this type
// is not a structure, simply return as the for-loop below
// will not work.
if info.kind != .Struct do return;
// 'members' is an array of Type_Info_Struct.Member that contains
// information about each member of a structure. Here, the name
// and type of the member are printed. Note, printf() already
// understands how to print a `type_expr`.
for info.members {
printf("{} is of type {}.\n", it.name, it.type);
}
}
Person :: struct {
name: str;
age: u32;
}
print_members(Person);
Refer to core/runtime/info/types.onyx
for what the fields of each kind of Type_Info are.