Draco-lang / Language-suggestions

Collecting ideas for a new .NET language that could replace C#
75 stars 5 forks source link

Goto, implicit loop labels #45

Closed LPeter1997 closed 2 years ago

LPeter1997 commented 2 years ago

The design of goto

There is a lot of hate for goto, but I believe there's no reason not to have it: it's a powerful feature, even if it's easy to abuse by beginners. I believe the C# syntax is pretty good here, we could stick to that:

foo: // label
    goto foo;

The usual rule would apply, you can't jump out of a function or into another function.

Break and continue

I believe break and continue are really weak features by themselves and having goto makes them completely redundant. That doesn't mean that they have no use, so I believe we could make a compromise. We could have loops implicitly define a break and continue label that the user can jump to with goto. Example:

func main() {
    for (i in Range(0, 10)) {
        if (i rem 2 == 0) goto continue;
        WriteLine("i is $i");
        if (i == 7) goto break;
    }
}

Generalizing labels

This gave me an idea: In the future, we could even generalize this to have more label expressions. For example, breaking out of a nested loop could be:

func main() {
    for (i in Range(0, 10)) {
        for (j in Range(0, 10)) {
            goto break(2);
        }
    }
}

This would have the added benefit of not having to define a label, even when you break from nested loops, which is a relatively common use for goto in C#.

WhiteBlackGoose commented 2 years ago

Alternative idea for break. Have one break, which quits the inner loop, and another break_outer, which quits the outer loop. It covers most of the cases, so e. g.

val arr2D = ...
for (...)
    for (...)
        if (found) break_outer;
arr2D[found] = None
DimitarBogdanov commented 2 years ago

Alternative idea for break. Have one break, which quits the inner loop, and another break_outer, which quits the outer loop. It covers most of the cases, so e. g.

val arr2D = ...
for (...)
    for (...)
        if (found) break_outer;
arr2D[found] = None

I think labels could solve this issue. In fact, a missing in C#, but found in Java feature is to give labels to loops, and be able to break/continue them, as such:

outer:
for (int x = 0; x < 16; ++x) {
    for (int y = 0; y < 16; ++y) {
        if (...) {
            break outer;
        }
    }
}

This is a counter-argument for not having break and continue: they're extremely useful when they have the right adjustments. This should not be difficult to implement, although I'm not intimately familiar with MSIL.

Edit: I believe this, combined with goto, is the perfect amount of tools a programmer would need in 99% of cases, without compromising on the syntax (goto continue is just plain weird for any newcomer from any C-style language).

Binto86 commented 2 years ago

I like this idea tho using labels might be bit confusing, i think about two options for this: 1) using attributes

#[LoopName("loopName")]
for(x in Range(0,10))
{
  break loopName
}

2) parameter of the for loop

for(x in Range(0,10), "loopName")
{
  break loopName
}

The first option is probably not good, i put it there just for completenes, but i really like the second one. For this to be easy to use for beginers i think that break with no argument breaks the curent loop just like in c# and this way u can have loop without defined name.

DimitarBogdanov commented 2 years ago

I agree the first option is pretty weird. I like the second one, but perhaps without the string quotes - just use a regular identifier in there. This means one check less in semantic analysis, which is always good, besides, the name is used as an identifier below.

I think examples from other languages should be looked into, maybe someone has come up with a very elegant syntax. I looked a little into this, came across the way Rust does it. Personally don't like it at all (the apostrophe is what is annoying me, but I reckon that's way easier to parse), but here it is anyway:

'outer: loop {
    println!("Entered the outer loop");

    'inner: loop {
        println!("Entered the inner loop");

        // This would break only the inner loop
        //break;

        // This breaks the outer loop
        break 'outer;
    }

    println!("This point will never be reached");
}

Other languages I looked into:

I reckon there's at least one language that has come up with a nice syntax that isn't a "special case to statement labels" situation, though!