Loop Match - a syntax for iterative evaluation

This iteresting idea was discovered in MoonBit Document

by the way, its grammar is really similar to Rust.

Its syntax like this

loop x, y{
    0, 0 => break 0 // return value
    a, b => continue <expr1>, <expr2> // start another match on <expr1>, <expr2>
}

This syntax may be useful in iterative evaluation, e.g. GCD.

fn gcd(x: i32, y: i32){
    loop x, y{
        0, g => break g,
        a, b => continue b, a%b
    }
}

Following code was from MoonBit.

fn sum_cons(xs: List[Int]) -> Int {
  loop xs, 0 {
    Nil, acc => break acc // break can be omitted
    Cons(x, rest), acc => continue rest, x + acc
  }
}

fn main(){
  println!(sum(Cons(1, Cons(2, Cons(3, Nil)))))
}

I wonder if this syntax is of use.

If would be of use in that it would likely work.

But such a feature has been proposed and rejected before, IIRC with the main reason that it doesn't add anything relative to loop { match ... { ... } }, while at the very least being evocative of debacles like gotofail, and possibly actually introducing a footgun that would make that possible.

4 Likes

can be written fairly similarly in current Rust:

fn gcd(mut x: i32, mut y: i32) -> i32 {
    loop { 
        match (x, y) {
            (0, g) => break g,
            (a, b) => (x, y) = (b, a % b)
        }
    }
}

(or, of course, in the natural tail recursive form that's almost certainly turned into a loop by the optimizer.)

10 Likes

A macro would be enough?

1 Like

This is the dictionary definition of an XY problem. Whats wrong with using the canonical version using recursion?

fn gcd(mut x: i32, mut y: i32) -> i32 {
    match (x, y) {
        (0, g) => return g,
        (a, b) => return gcd(b, a % b)
    }
}

Not for the likely reason that OP wants this: to reduce rightward drift in code. Rust makes it especially easy to be 4+ indentation levels deep in regular code, not least because the body of a method starts at column 8 i.e. when you start writing it you're already 2 indentation levels deep.

Maybe instead match should be supported anywhere that expects a block?

fn gcd(x: i32, y: i32) -> i32 {
    loop match (x, y) {
        (0, g) => break g,
        (a, b) => continue b, a % b,
    }
}

Which will also reduce indentation for the recursive version:

fn gcd(mut x: i32, mut y: i32) -> i32 match (x, y) {
    (0, g) => return g,
    (a, b) => return gcd(b, a % b),
}
1 Like

Can also be a regular, non-macro function: Rust Playground

1 Like

I don't think rightward drift is a big deal though. Everyone has widescreen monitors anyways nowadays. No, using monitors in portrait mode isn't practical. I tried it once with a dual monitor setup.

Also, if it goes too far, that is a sign you ought to reactor to use helper functions or restructure the code in some other way.

2 Likes

Obligatory mention of previous loop-match threads, like Allow the infinite `loop` keyword to prefix expressions w/ blocks - #6 by scottmcm

As for this loop syntax specifically, I don't think it'll happen. There's long been discussion of a dedicated finite state machine syntax that might support some use of continue kinda like this, but I expect loop itself will still stay the same.

The only problem with doing this with recursion is that Rust doesn't have guaranteed tail calls, but the solution there is to add those instead. (And people are indeed already working on them: Support tail calls in mir via `TerminatorKind::TailCall` by WaffleLapkin · Pull Request #113128 · rust-lang/rust · GitHub )

2 Likes

You're forgetting about laptops. A max line width of 80 chars is still useful, and will be as long as 14" to 16" displays are used for software development.

That said, I'm not actually arguing for loop match.

2 Likes

I think the main problem is is that you are combining two concerns that should be separate:

  1. The ergonomics of a match that is the only expression of a block.
  2. A way to pass values to the next iteration of a loop without using mut.

I'm also not arguing for (or against!) loop match, but count me as another voice for continuing to stick to 80 columns as a hard max, because 80 columns lets me use a comfortable font size and still get two files of code side by side on my screen (or even three files if the monitor is really wide).

For natural-language text, there's actual studies showing that people read faster and more smoothly if the text block is kept at about 10–12 words per line, or even narrower for people with dyslexia. (Linked article focuses on the dyslexic case, see the last paragraph of page 10 and reference 33 for neurotypical readers.) That corresponds to 60–70 characters per line for English. Code is formatted differently enough that you can't directly apply that observation but I think it's still relevant.

6 Likes