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);
}
}
}