Skip to content

Conversation

@Keno
Copy link
Member

@Keno Keno commented Dec 11, 2025

This implements multi-level and labeled break as contemplated in #5334. In addition this adds support for break-with-value (#22891) as well as for-then (aka for-else #1289). Also while-then of course. All three features are syntax gated to 1.14 syntax.

The syntax for multi-level break is as follows:

for i = 1:10
  for j = 1:10
    break break (i, j)
  end
end # evaluate to `(1,1)

The break value can be continue in which case the next innermost loop is continued:

julia> for i = 1:3
         for j = 1:3
           i > 1 && j == 2 && break continue
           @show (i,j)
         end
         @show i
       end
(i, j) = (1, 1)
(i, j) = (1, 2)
(i, j) = (1, 3)
i = 1
(i, j) = (2, 1)
(i, j) = (3, 1)

For more deeply nested loops, the loop can be annotated with a @label and the the break or continue can be targeted using @goto:

julia> @label outer for a = 1:2
         for b = 1:3
           for c = 1:4
             b > 1 && c == 2 && @goto outer break
           end
           @show b
         end
         @show a
      end
b = 1

Naturally continue is supported here as well.

The syntax and semantics for for-then are as proposed in the issue:

function has5(iter)
  return for x in iter
    x == 5 && break true
  then
    false
  end
end

Any break (including multi-level and labeled) skips the corresponding loop's then block, which ordinarily would run at loop completion.

I think the then keyword deserves some bikeshedding. I think the else keyword is seen as a bit of a mistake in languages that have it. Common lisp uses finally but I wanted to avoid it here since the semantics are substantially different from finally in a try-catch block (as mentioned in the original issue. Perl has a similar feature with a continue block (but it runs every time). Claude points out that django templates have an empty clause in for loops that runs if and only if the collection is empty (but doesn't have break in any case).

Another keyword options I thought about is normally. Kind of like finally, but for normal termination.

An additional motivation here is to make multi-level break available as syntax for the potential future addition of match (#60344).

Written by Claude. Some remaining cleanup and todos, but appears to basically be working.

@gbaraldi
Copy link
Member

I sent this to keno but I think it's a useful reference for anyone discussing this https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3568r0.html. This is the C++ proposal and it takes a look at what other languages do and what makes sense or not.

For my personal opinion I think that labels should be required for this feature, this maybe means we should introduce proper label syntax in Julia (which I'm not against). But forcing the use of a label means that code that was written like

for cond1
   for cond2
       ...
       break break
    end
 end

if over time the two loop statements get moved away from each other and then a new loop nesting is added, it continues to be clear and avoids the bug of jumping to the wrong place.

Also by always needing labels you can naturally extend to being able to break out of any kind of block, which may remove the need for a then.

This implements multi-level and labeled break as contemplated in #5334.
In addition this adds support for break-with-value (#22891) as well
as for-then (aka for-else #1289). Also while-then of course.
All three features are syntax gated to 1.14 syntax.

The syntax for multi-level break is as follows:
```
for i = 1:10
  for j = 1:10
    break break (i, j)
  end
end # Loop evaluate to `(1,1)
```

The break value can be `continue` in which case the next innermost
loop is continued:

```
julia> for i = 1:3
         for j = 1:3
           i > 1 && j == 2 && break continue
           @show (i,j)
         end
         @show i
       end
(i, j) = (1, 1)
(i, j) = (1, 2)
(i, j) = (1, 3)
i = 1
(i, j) = (2, 1)
(i, j) = (3, 1)
```

For more deeply nested loops, the loop can be annotated with a
`@label` and the the break or continue can be targeted using `@goto`:

```
julia> @Label outer for a = 1:2
         for b = 1:3
           for c = 1:4
             b > 1 && c == 2 && @goto outer break
           end
           @show b
         end
         @show a
      end
b = 1
```

Naturally `continue` is supported here as well.

The syntax and semantics for `for-then` are as proposed in the
issue:
```
function has5(iter)
  return for x in iter
    x == 5 && break true
  then
    false
  end
end
```

Any `break` (including multi-level and labeled) skips the corresponding
loop's `then` block, which ordinarily would run at loop completion.
@Keno Keno force-pushed the kf/breakbreakbreakcontinue branch from 88eedc4 to 0df79d3 Compare December 11, 2025 16:38
@Keno
Copy link
Member Author

Keno commented Dec 11, 2025

Yeah, as I said on slack, I think that's a reasonable way to go as long as we have proper syntax for labeled blocks. Rust's syntax of 'label: is currently a syntax error, so we could just steal it. I'll have claude code up that version as well (but probably not until next week).

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

Successfully merging this pull request may close these issues.

3 participants