Commentary
Parameterized types (generics)
Because type paramters work at compile-time and not runtime, we need a better
syntax for them. Ki uses @ for compile-time info, so something like:
type StringArray(array(@type: string)) {}
Specifying these in a struct works like this:
struct GameStruct(@human_type, @monster_type, @field_type) {
var human: @human_type
var monster: @monster_type
var field: @field_type
}
:fields works the same way:
type Game(@human_type, @monster_type, @field_type) {
:fields {
var human: @human_type
var monster: @monster_type
var field: @field_type
}
}
Parameterizing a type:
struct GameStruct(@human_type, @monster_type, @field_type) {
var human: @human_type
var monster: @monster_type
var field: @field_type
}
type Game(GameStruct(@human_type: Human, @monster_type: Monster, @field_type: Field)) {}
array GameArray(@type: Game)
This is only for parameterizing though. If you want to build a type on a base type without parameterization you have to use the full syntax:
uint Counter {} # Bad
type Counter(uint) {} # Good
@globals
I think @globals can't be a thing. Unless there's some special function that
initializes it before main runs, it will have uninitialized fields. I guess
we could require with to access them, but fuck that's terrible.
range
Empty ranges are a thing:
fn main() {
range indices: [0..0)
}
Naming
I think maybe the higher-level types (array, dynarray, string,
dynstring, thread) should be capitalized. Scalars can remain lowercased.
Returning Values vs. Pointers
Unlike C, Ki functions can return values. This is what that looks like in C:
void build_person(Person *p) {
p->name = "Charlie";
p->age = 33;
}
int main(void) {
Person p;
build_person(&p);
}
Here, p is an output parameter to build_person. So essentially, the
calling function pre-allocates the data somehow, then always passes a pointer
to it as an output parameter to the called function.
OK, that's fine. Errors work... similarly?
void build_person(Person **p, Error *e) {
*p = malloc(sizeof(Person))
if (!(*p)) {
e->occurred = true;
e->msg = "Failed to allocate memory";
e->code = 10;
return;
}
p->name = "Charlie";
p->age = 33;
}
int main(void) {
Person *p = NULL;
Error e = { 0 };
build_person(&person, &e);
if (e.occurred) {
fprintf(stderr, e.msg);
exit(e.code);
}
}
With error handlers:
void build_person(Person **p, Error *e) {
*p = malloc(sizeof(Person))
if (!(*p)) {
e->occurred = true;
e->type = ERROR_TYPE_ALLOCATION_FAILURE;
e->msg = "Failed to allocate memory";
e->code = 10;
return;
}
p->name = "Charlie";
p->age = 33;
}
int main(void) {
Person *p = NULL;
Error e = { 0 };
build_person(&person, &e);
if (e.occurred) {
ErrorHandler *eh = error_lookup_handler(e.type);
if (eh) {
eh(&e);
}
else {
fprintf(stderr, e.msg);
exit(e.code);
}
}
}