The rebellion/type module is broken down into a collection of modules, such as rebellion/type/record and rebellion/type/tuple. Each module is meant for working with a specific kind of data type. However, all of these modules have a few things in common.
Any type created in rebellion/type is a nominal type, not a structural type. This means creating a new type, such as with define-record-type, creates a new named unique type that is distinct from all other types. The functions created by one use of define-record-type will not work on instances created via another use of define-record-type, even if both types are named the same and have exactly the same fields.
All types are created using Racket structure types, and the created struct types can have structure type properties attached to them. Each module typically provides default structure type properties for the types it creates, based on how its types are typically used. These defaults can be freely overriden when desired.
The structure, or shape of a type is distinct from an implementation of that type. Each rebellion/type module reflects this distinction by providing two different interfaces for shapes and implementations. A rebellion/type module for working with kind types provides:
A kind-type? type which describes the shape of a kind type but does not provide any constructors, accessors, or predicates. Two kind-type? values are equal whenever the names, field counts, etc. of the types they describe are equivalent. That is, kind-type? values are compared using structural equality. The term shape of a type or simply shape, refers to a kind-type? value. When used without any qualification, the term type typically refers to such a value as well.
A kind-descriptor? type which provides an actual implementation of a kind type, including a predicate and any relevant constructors and accessors. Such an implementation is called a type descriptor, and allows generically operating on unknown types at runtime. At runtime, the term implementation of a type, or simply implementation, refers to a type descriptor.
A kind descriptor can be created for a type using the make-kind-implementation function provided by the corresponding rebellion/type/kind module. Multiple calls to such a function with the same type will produce distinct implementations that are not equal? to each other, meaning that the rebellion/type modules create generative types.
Type descriptors may be uninitialized. An uninitialized type descriptor cannot be used to create or interact with instances of the type until after initialization is complete. Uninitialized type descriptors are fairly rare: they can only be obtained via the #:property-maker mechanism for specifying structure type properties of created types. This is because the property-making function needs to receive the type descriptor in order to return implementations of type properties, but this happens before the type is created. Property makers can’t use the descriptor’s constructor and accessor immediately, but they can refer to them in implementations of properties such as prop:custom-write (since by the time the constructor or accessor is actually used, the type is created and the descriptor is initialized). This delayed initialization step is necessary to allow property makers to be defined without mutual recursion and in separate modules from the type definitions they’re used for, allowing reuse of generic property makers.
Each rebellion/type/kind module provides a define-kind-type form that creates a new type and binds its constructor, predicate, and accessors to variables. These forms are how most users of the rebellion/type modules are expected to create types, similar to how most Racket struct types are created with the struct form rather than the dynamic make-struct-type function.
Some types encapsulate compile-time information about their name, size, fields, and other properties inside a type binding. Type bindings are used by macros such as enum-out to generate code specialized to that type. Type bindings are created by type definition macros and extracted using syntax classes. For example, define-enum-type creates a type binding using define-syntax and the corresponding syntax class enum-id extracts that binding.
At present, not all kinds of types support type bindings —