When creating your own interpreted programming language, you have a choice to make as to how you represent your program data cells.
If you want to directly use the base language’s types with a tagged union, your cells are necessarily going to get fairly large, unless you make many of them references. If you want small cells, you have to roll your own base types. This gives more flexibility in the long run, at the expense of a little more work up front.
You can make a cell one word (8 bytes) and have a lot of functionality.
source module: 2 bytes
base type: 2 bytes (either universal or per module)
item data: 4 bytes
“Item data” can be either 32 bit data (i32, f32, rune, etc.) or an indexed reference to a dynamic array of that type.
You can use negative indexing as a single bit flag. For example, use a negative base type to indicate a reference to that type.
If you want more data items than this allows, use a two word cell. That gives you 4 bytes for metadata. You can even hold short strings directly inside the cell. There are any number of variations on this concept. Pick one that works for you and play with it.