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

Proposal: New labeled break syntax, removing colon prefix by reusing => token, #5083

Closed
ghost opened this issue Apr 17, 2020 · 15 comments
Closed
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@ghost
Copy link

ghost commented Apr 17, 2020

New syntax, overview:

break;                // normal
break => value;       // when returning a value from the current block
break label;          // when breaking a labeled block
break label => value; // when returning a value from labeled block
// break :label;      <-  the colon prefix would not be necessary anymore.

Pros:

  • Get rid of colon prefix. reuse => instead
  • More intuitive and easier to remember (at least my opinion)
  • blk: { ... } or outer: while(... stays as before
  • Simple change to grammar/syntax only. No change to semantics.
  • Would be more consistent with switch(cond) { true => 5, else => 10,};

Cons:

  • Two more characters to type. In my opinion a non-issue.

Example usage, taken from examples in the language reference:

fn rangeHasNumber(begin: usize, end: usize, number: usize) bool {
    var i = begin;
    return while (i < end) : (i += 1) {
        if (i == number) {
            break => true;
        }
    } else false;
}


test "nested break" {
    outer: while (true) {
        while (true) {
            break outer; // no '=>', so outer has to be a label
        }
    }
	// ...
}

test "labeled break from labeled block expression" {
  var y: i32 = 123;
  const x = blk: {
	  y += 1;
	  break blk => y; // imagine blk with same syntax highlighting color as `blk:` here
  };
}
@ghost ghost changed the title Proposal: New labeled break syntax, removing colon prefix by resuing => token, Proposal: New labeled break syntax, removing colon prefix by resusing => token, Apr 17, 2020
@Vexu Vexu added the proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. label Apr 17, 2020
@Vexu Vexu added this to the 0.7.0 milestone Apr 17, 2020
@ghost ghost changed the title Proposal: New labeled break syntax, removing colon prefix by resusing => token, Proposal: New labeled break syntax, removing colon prefix by reusing => token, Apr 17, 2020
@squeek502
Copy link
Collaborator

More intuitive and easier to remember (at least my opinion)

I personally have to think "where does the colon go again?" pretty much every time I write a labeled break.

@Tetralux
Copy link
Contributor

Tetralux commented Apr 18, 2020

Food for thought: Odin's original idea for returning a value from a control construct was to use give:

const val = {
    give 32;
};
assert(val == 32);
const val = blk: {
    if (true) {
         give blk 32;
    }
};

Certainly though, the :blk always seemed awkward to me too.

@jakwings
Copy link

Food for thought: Odin's original idea for returning a value from a control construct was to use give

Right now only loop control has an anonymous label, then why not assign an anonymous label to assignment as well?

const x = blk_x: if (...) {
    ... // a few lines of code
    break => option_a;
} else {
    ...  // many lines of code
    break blk_x => option_b;
};

const y = blk_y: {
    ...
    const tmp = while (...) {
        ...
        if (...) break => loop_result;
        ...
    };
    ...  // many lines of code
    break blk_y => result;
};

@Tetralux
Copy link
Contributor

@iology I'm not sure I follow.

@jakwings
Copy link

@Tetralux No problem. I shouldn't say the assignment is labeled anonymously. :P

I have just found out that break can already work with any labeled blocks {...} so what I really want is:

  1. support outer_label: for/while/if/switch (...)

  2. add an anonymous label to the blocks binded to an assignment:

    const x = (anonymous label:) {
        ...
    };
    
    const x = (anonymous label:) if {
        ...
    } else {
        ...
    };
    

Sorry for the confusion!

@donaldcallen
Copy link

I have no idea whether this is feasible in zig, but I would suggest considering how Rust does this: the presence or absence of a semi-colon after the last expression in a block determines whether the block returns the value of the expression or not.

@SpexGuy
Copy link
Contributor

SpexGuy commented Apr 20, 2020

@donaldcallen That was how Zig worked originally, but it was changed because it made it hard to tell the difference between a block returning the result of an if/while/for expression and a block with an if/while/for statement at the end. Here's the original issue.

@ghost
Copy link
Author

ghost commented Apr 20, 2020

I see there are some (-1) reactions to this issue, but no explanations yet as to why it might be better to stick with status quo.

@fengb
Copy link
Contributor

fengb commented Apr 28, 2020

How about we just make the colon consistent?

const x = blk: {
    y += 1;
    break blk: y;
}
// or
const x = :blk {
    y += 1;
    break :blk y;
}

@squeek502
Copy link
Collaborator

How about we just make the colon consistent?

Colon after the label would look strange when not breaking with a value:

blk: {
    y += 1;
    break blk:;
}

and colon before the label would look a bit strange for e.g. labeled loops:

:outer while (true) {
    while (true) {
        break :outer;
    }
}

@andrewrk andrewrk modified the milestones: 0.7.0, 0.8.0 Oct 27, 2020
@cajw1
Copy link

cajw1 commented Nov 19, 2020

Edit: kindly ignore this. You could also require label names to start with a special sigil to gain conciseness and ditch the colon entirely:

break;                 // normal
break value;           // when returning a value from the current block
break #label;          // when breaking a labeled block
break #label value;    // when returning a value from labeled block
break #;               // maybe

#label { ... break #label ... }  // no colon required to label a block
# { ... break # [val]... }          // allowed for conciseness (binds to innermost #)

@DutchGhost
Copy link

How about we just make the colon consistent?

const x = blk: {
    y += 1;
    break blk: y;
}
// or
const x = :blk {
    y += 1;
    break :blk y;
}

I sort of think of blk: and break :blk that there is something in between those two :'s, and that it thus makes sense to at the declaration of the block, have it after the block name, and when you break out, have it before the block name. I think that it feels quite symmetric this way, even though it may be unintuitive at first. But mayhaps better documentation on where the : goes at which place would help quite a bit for newer people.

@Tetralux
Copy link
Contributor

Tetralux commented Nov 30, 2020

But mayhaps better documentation on where the : goes at which place would help quite a bit for newer people.

Worth noting that I fairly often get it the wrong way around, despite not being new to Zig.


Perhaps we can just remove the colon from the break usage?

const val = blk: {
    break blk 31;
};

This cannot be confused with breaking with a single value, because there's two values given to break, plus, you presumably couldn't have another variable called blk, because of the shadowing rules.

Further, this also makes things more consistent, since, as an illustration:

const Int = @IntType(32, false);

const i: Int = 40; // We don't have to specify `@` anywhere here, despite using `Int`!
                   // The name has been bound, and that's that.
const val = blk: {

    break blk 42; // We don't have to specify `:` anywhere here, despite using `blk`!
                  // The name has been bound, and that's that.
}

So there's symmetry to be gained here.

@yohannd1
Copy link

yohannd1 commented Apr 18, 2021

I like the look of that. We could also extend this to anonymous blocks (an equivalent to the break => value, I suppose):

const val = { // This could also be a `.: {` or a `: {` to indicate it can be broken with
              // a non-void value, not sure.
    break . 42; // `.` means "this specific block", or an equivalent "infer the nearest
                // parent block".
};

std.testing.expect(val == 42);

@andrewrk andrewrk modified the milestones: 0.8.0, 0.9.0 May 19, 2021
@ghost ghost mentioned this issue Sep 14, 2021
@andrewrk andrewrk modified the milestones: 0.9.0, 0.10.0 Nov 23, 2021
@andrewrk andrewrk modified the milestones: 0.10.0, 0.11.0 Apr 16, 2022
@andrewrk andrewrk modified the milestones: 0.11.0, 0.10.0 Apr 18, 2022
@FeldrinH
Copy link

Was this proposal rejected? Are there any open proposals for a cleaner syntax to return values from blocks?

I feel like the current syntax is really bad for readability in particular due to having to keep track of a label that might be declared dozens of lines away. I would really like to see this syntax changed to something that allows returning values without a superfluous label.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

No branches or pull requests