Skip to content

Onyx Type Info

Brendan Hansen edited this page Sep 1, 2022 · 1 revision

Type Info

Introduction

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)

How to use

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_exprs 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.

Clone this wiki locally