digitalmars.dip.ideas - Action blocks / Blocks with results
- Quirin Schroll (59/59) Aug 06 An action block is a statement block with a result. It’s
- Richard (Rikki) Andrew Cattermole (19/19) Aug 06 I've been thinking about this a bit.
- Nick Treleaven (5/10) Aug 06 Already answered:
- Nick Treleaven (37/49) Aug 06 It seems an unnecessary constraint to require declaring a
An action block is a statement block with a result. It’s
introduced via `do` and a statement block, followed by a result
expression that is the result of the action block.
Example:
```d
const x = do { int y = 0; ++y; } y;
assert(x == 1);
```
This initializes `x` to 1. The variable `y` declared in the block
is in scope for the result expression, but not afterwards. This
eliminates the need for a yield or return keyword entirely.
Blocks whose declarations leak out have precedent in `for` loops.
A simple mental model is a `do {…} while(false)` loop with an
added result.
The `break` keyword can be used to skip the rest of the action
block and continue with evaluating the result expression.
```d
const x = do { int y = 0; break; ++y; } y;
assert(x == 0);
```
The action block can be labelled by placing an identifier between
`do` and the opening brace. That allows to break out of any block
early:
```d
const x = do xInit {
const y = 0;
const y0 = do {
int z;
break xInit;
} z;
} y;
```
This jump forward cannot skip the initialization of any variable
used in the result expression of the action block that’s ended:
```d
const a = do {
break; // error, skips initialization of b
int b;
} b;
const x = do xInit {
const y = do {
int z;
break xInit; // error, skips initialization of y;
} z;
} y;
```
A `break` is conceptually more like `goto`.
The benefits is that it both allows initializations of
non-mutable variables and allows control-flow statements such as
`return`, `continue`, and `break` to affect the enclosing
function. An immediately invoked lambda cannot have the latter
ones. Another benefit is keeping scopes of local variables small.
It should be guaranteed that the result of a action block is not
copied to its result. It’s directly constructed into it (for
initializations) or moved (for assignments).
Maybe: A `ref do` action block yields its result by reference.
That result cannot be local to the action block, of course, but
the result could be an lvalue expression that uses local
variables of the action block as parameters.
Aug 06
I've been thinking about this a bit.
At the very least the label support can be removed. A label at the end
of scope + a goto will do the same job but with a more familiar syntax.
Next I'm not convinced that you have the return value right.
A better syntax might be:
```d
Type got = do(Type var) {
};
```
The reason I am suggesting this might be better is due to how its going
to have to be implemented in the compiler. Using comma expression.
```
ScopeExpression {Comma(VarDeclaration declared, ScopeExpression { ... },
declared)}
```
I'm pretty sure there is no such thing as ``ScopeExpression``, so that
alone is gonna screw this proposal over.
Finally I have a question, why do we need this if you can use a function
literal in its place?
Aug 06
On Wednesday, 6 August 2025 at 17:50:53 UTC, Richard (Rikki) Andrew Cattermole wrote:Finally I have a question, why do we need this if you can use a function literal in its place?Already answered:allows control-flow statements such as return, continue, and break to affect the enclosing function. An immediately invoked lambda cannot have the latter ones.For break read `break label;` where the label is outside the `do` block. Same goes for `goto label;`.
Aug 06
On Wednesday, 6 August 2025 at 17:20:54 UTC, Quirin Schroll wrote:
An action block is a statement block with a result. It’s
introduced via `do` and a statement block, followed by a result
expression that is the result of the action block.
Example:
```d
const x = do { int y = 0; ++y; } y;
assert(x == 1);
```
This initializes `x` to 1. The variable `y` declared in the
block is in scope for the result expression, but not
afterwards. This eliminates the need for a yield or return
keyword entirely.
It seems an unnecessary constraint to require declaring a
variable (or reusing/assigning to an existing variable) for the
result, rather than being able to yield an rvalue. I suggest
something like this syntax:
```d
const x = do { int y = 0; ++y; out y; }; // 1
const z = do {
if (x < 0) out 0;
log("x >= 0");
out x + 1;
}; // 2
```
This would be useful as a statement too, to prevent the 'triangle
if' pattern:
```d
do {
if (a) break;
b = e1;
if (b) break;
c = e2;
if (c) break;
code;
};
```
vs:
```d
if (!a) {
b = e1;
if (!b) {
c = e2;
if (!c) {
code;
}
}
}
```
Aug 06









Nick Treleaven <nick geany.org> 