wareya / konoran

A low-level programming language, designed as a compiler target
Other
7 stars 0 forks source link

Small readability suggestion for the int types #1

Open redbar0n opened 3 months ago

redbar0n commented 3 months ago

FWIW, here's just a small suggestion for improved readability for the int types.

The following konoran code from the Readme is a bit hard to quickly scan visually:

    u64 i = 0u64;
    f64 yvel = 0.0f64;
    f64 y = 0.0f64;
    f64 gravity = 9.8f64;
    f64 delta = 0.001f64;

Having the variable name at the start of the line would help (if you want to see where a specific variable came from):

    i: u64 = 0u64;
    yvel: f64 = 0.0f64;
    y: f64 = 0.0f64;
    gravity: f64 = 9.8f64;
    delta: f64 = 0.001f64;

Maybe even better, would be to infer the variable type from the declared value, since the information is already there at the end (so declaring it additionally seems redundant).

    i = 0u64;
    yvel = 0.0f64;
    y = 0.0f64;
    gravity = 9.8f64;
    delta = 0.001f64;
wareya commented 3 months ago

I've considered adding a var keyword for full definition-declarations, but I'm still unsure about it, because it might make things more complicated for any possible reimplementations:

    var i = 0u64;
    var yvel = 0.0f64;
    var y = 0.0f64;
    var gravity = 9.8f64;
    var delta = 0.001f64;

The example might also just benefit from vertical alignment, like:

    u64 i       = 0u64;
    f64 yvel    = 0.0f64;
    f64 y       = 0.0f64;
    f64 gravity = 9.8f64;
    f64 delta   = 0.001f64;
redbar0n commented 3 months ago

Yeah, I also thought vertical alignment would make it really readable, but apparently it's a pain to emit whitespace from a compiler (like for proper Python indentation).

Not sure about how var declarations would help and not be redundant.

Another thing I thought about is that it's very hard to visually tell u64 apart from f64 (especially if they come at the end of a number like in your last example there). Which could lead to bugs if humans were to read/write the code. One idea could be to make one the default, so you only have to specify it if it deviates from that.

Like, if f64 was the default, it could be:

    i = 0u64;
    yvel = 0.0;
    y = 0.0;
    gravity = 9.8;
    delta = 0.001;
wareya commented 3 months ago

I want to stay with explicit declarations a hundred percent; my experience with implicit declarations in Python was awful. Rather than being redundant, explicit declarations make it clear when outer symbols (e.g. globals, function names) become shadowed by inner variables.

One thing I considered for the suffixes was making them a separate lexer/parser token, e.g. 0 u64 and 143.01 f64, but I'm very unsure about it. It "looks", visually, like it might be casting syntax, even though it wouldn't be.

redbar0n commented 3 months ago

Oh, yes, because something denoting an explicit declaration would differentiate it from variable reassignment.

If indentation could be fixed easily somehow (compiler output, or IDE plugin), then I think this would be ideal:

i:       u64 = 0;
yvel:    f64 = 0.0;
y:       f64 = 0.0;
gravity: f64 = 9.8;
delta:   f64 = 0.001;

Here they all have 9 chars including whitespace before the f64 specification. With a monospaced font it looks great (ironically the github editor is not monospaced but its output is).

So you don't need the redundant suffixes. But you'd need them when defining a new value inline, like in the Readme example:

yvel = yvel + delta*gravity*0.5f64;

Making f64 it a separate lexer/parser token here would complicate that example:

yvel = yvel + delta*gravity*0.5 f64;

Maybe 0.5_f64 is a good compromise, which makes the value itself a bit more readable, while keeping the grouping.

If you want to keep the declaration of values (like 0.5_f64) always the same in both definitions and usage, then this might work:

i       := 0_u64;
yvel    := 0.0_f64;
y       := 0.0_f64;
gravity := 9.8_f64;
delta   := 0.001_f64;

Because then you could use the convention from other languages that := denotes a declaration, whereas = is reserved for reassignment. That way, you'd also open up for having somevarname: sometype = somevalue for other data types where the type couldn't be inferred from the value.

Then the original example from the Readme:

f32 func_gravity()
{
    u64 i = 0u64;
    f64 yvel = 0.0f64;
    f64 y = 0.0f64;
    f64 gravity = 9.8f64;
    f64 delta = 0.001f64;

head:
    yvel = yvel + delta*gravity*0.5f64;
    y = y + yvel*delta;
    yvel = yvel + delta*gravity*0.5f64;

    i = i + 1u64;

    if (i < 500000000u64)
        goto head;

    return (y) as f32;
}

Would look like:

f32 func_gravity()
{
    i       := 0_u64;
    yvel    := 0.0_f64;
    y       := 0.0_f64;
    gravity := 9.8_f64;
    delta   := 0.001_f64;

head:
    yvel = yvel + delta*gravity*0.5_f64;
    y = y + yvel*delta;
    yvel = yvel + delta*gravity*0.5_f64;

    i = i + 1_u64;

    if (i < 500000000_u64)
        goto head;

    return (y) as f32;
}

Or even make it uniform with the casting syntax:

f32 func_gravity()
{
    i       := 0 as u64;
    yvel    := 0.0 as f64;
    y       := 0.0 as f64;
    gravity := 9.8 as f64;
    delta   := 0.001 as f64;

head:
    yvel = yvel + delta*gravity*(0.5 as f64);
    y = y + yvel*delta;
    yvel = yvel + delta*gravity*(0.5 as f64);

    i = i + (1 as u64);

    if (i < 500000000 as u64)
        goto head;

    return (y) as f32;
}

Anyways, these are just some ideas, take from it what you will. It might very well be that the original was better, taking all things into consideration. The language being aimed at being compiler output, then it may be more worth it to have slightly less human readable syntax, if it makes the compilers outputting the syntax simpler and easier to implement.