Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discontinuity in if vs loop syntaxes #1

Open
Magnogen opened this issue Oct 13, 2022 · 4 comments
Open

Discontinuity in if vs loop syntaxes #1

Magnogen opened this issue Oct 13, 2022 · 4 comments

Comments

@Magnogen
Copy link

At the moment, you'd need to scan down to the end of a block to see if it loops, whereas with an if, you can tell right from the start that the following scope can have a possibly of not running, as well as what condition is required to make it do so.

Perhaps instead of putting the loop; at the end of the block, it would be easier to read putting it near the start. Perhaps something like:

fn foo() -> i32
{
	var x = 0;

	loop {
		x = x + 1;
	}

	return: x
}

(example taken from your readme).

Another option is, instead of using loop, you could instead use the word forever as it gives more of an idea as to what happens.

I can understand why not though, as goto statements go at the end of the code to repeat. But then again if that's the case, why not use this syntax and remove loop altogether?

fn foo() -> i32
{
	var x = 0;

	increment: {
		x = x + 1;
		goto increment;
	}

	return: x
}
@SLiV9
Copy link
Owner

SLiV9 commented Oct 13, 2022

Yeah I had considered loop { ... } because that's also the syntax used in other languages. The choice for loop; is mostly aesthetics, as I don't want it to look too similar to a for loop, because then it begs the question why it isn't. Whereas loop; is more symmetric with goto end; in that both are "jump statements" instead of language constructs.

Additionally, the current syntax gels well with if statements:

if x == y
{
    // some other code
    loop;
}

is allowed, so you don't need to do this:

if x == y
{
    loop {
        // some other code
    }
}

But I like your idea of using a label at the start of a codeblock to signify that it loops. What do you think of this?

{
    increment:
    // some other code
    goto increment;
}

The reason I put the label inside the braces is that goto statements are currently not allowed to jump backwards, but "a goto at the end of a block may jump to a label at the start of that block" could be a nice exception to that rule, given that the label's scope starts and ends at the {.

@Magnogen
Copy link
Author

That's some great reasons, I can understand your thought process. Why can't gotos jump backwards? Is it like a lint bug, that can easily result in infinite loops, so it's banned altogether?

Love what you're doing with this language too btw, I've been intrigued by gotos ever since I read about them in the goto Design Note on Crafting Interpreters, which also has a really tidy use case for goto as well.

@SLiV9
Copy link
Owner

SLiV9 commented Oct 16, 2022

The main reason is to try to keep the code understandable. Early return and breaking out of a nested loop are two valid use cases for gotos, and they both only require jumping forward. I think backwards jumps, especially when they cross eachother like

// ...
a:
// ...
{
    b:
    // ...
    if x == y
        goto a;
}
// ...
if u == v
    goto b;

get very unintuitive very quickly.

Jumps that are not attached to their own scopes (like for and if are) also introduce semantical issues that other language constructs don't really have. Although I'll admit I didn't really think about them until after I settled on goto and loop. Take the following example:

var x = foo();
var p: &i32 = &x;
foo:
var y = x + 1;
if x == 0
{
    &p = &y;
    x = x + 1;
    goto foo;
}

This is perfectly valid if foo() returns something other than 0, but what does it mean for var y = x; to be executed twice? And what value does p point to in that case? I assume in practice a C compiler will put y in the same place on the stack twice, so it will "work", but it doesn't feel great. At least with

var x = foo();
var p: &i32 = &x;
{
    var y = x + 1;
    if x != 0
        goto end;
    &p = &y;
    x = x + 1;
    loop;
}
end:

you can point to the } where the scope of y ends and say that the line &p = &y; is invalid or UB.

Anyway that's just me rambling. Glad that you like the language.

@Magnogen
Copy link
Author

That's a really interesting thought process, I never thought about combine scopes with gotos before, it kind of makes sense, but at the same time, I kind of doesn't.

I supppose with the loop syntax you currently have (as opposed to using a goto to explicitly go back) makes a lot more sense with scoping, but if the user were to try and explicitly go back for whatever reason and result in defining y again, it would usually result in an error, like this program:

var x = foo();
var p: &i32 = &x;
var y = x + 1;
if x == 0
{
    &p = &y;
    x = x + 1;
    var y = x + 1;
    // Error: "y" is already defined
    if x == 0
    {
        &p = &y;
        x = x + 1;
        // more nested 'iterations'
    } 
}

And that's essentially how the goto would work from another perspective.

And creating a whole new "call frame" for each goto is definitely not the way to go, but that would solve this problem.

Certainly an interesting conundrum. And you're welcome! ^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants