www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Idea: Qualified jumps

reply Quirin Schroll <qs.il.paperinik gmail.com> writes:

Imagine you have a loop that does multiple things slightly 
differently. You think about refactoring those similar parts into 
a single function that takes a few parameters and could even have 
a nice, descriplive name. And you can’t — because there's a pesky 
`return` or `break` statement that just wouldn't work when put 
into another function. How would it even work anyways? Well, how 
would it? It couldn’t ever work in a global-scope function, 
that’s for sure, but it could — in principle — work in a nested 
function or a lambda under some reasonable conditions.
I've thought some time about the syntax, and `parnet.return 
value` or `parent.break label` (i.e. ⟨qualified-name⟩.⟨statement⟩ 
⟨args⟩) felt the best, so I'm using. It is borrowed from 
`object.BaseClass.method` syntax.


Show, don’t tell:
```D
int parent(int[] xss)
{
     loop:
     foreach (xs; xss)
     {
         foreach (x; xs)
         {
             void child(int y)
             {
                 if (isPrime(y))
                     parent.break loop;
                 else if (y == 0xF00D)
                     parent.return 1234;
                 else if (y > 1_000_000)
                     parent.goto veryBig;
             }
             child(x);
             higherOrderFunc(&child, x);
         }
     }

     return 0;

veryBig:
     writeln("wow!");
     return 1;
}
```
The function `child` must be declared in the loop. Declare it 
before and it does not see the label `loop` (you could object to 
this). Declare it after and its name is not avaliable inside the 
loop (this has always been the case).

The reasonable conditions are: If you ever take the address of 
that function, it must be assigned to a `scope` variable.
Otherwiese when the function is called, the stack frame it 
referred to might not exist anymore. It cannot be returned by the 
parent function. And it cannot be `static`.

The only case without ⟨args⟩ is `return` in a `void` context. 
`break` and `continue` must use the labelled form.


If we go further and deprecate lambdas that start with a brace 
(i.e. `auto lam = {}` lambdas, not `auto lam = (){}` lambdas), we 
could change their semtantics: Every `return`, `break`, 
`continue`, and `goto` statement would be implicitly a 
`parent.return`, `parent.break` etc. targeting the nearest 
control-flow structure.

Together with some proposal to omit parentheses when the only (or 
last) argument is such a special lambda, it would allow for 
custom control-flow statements.

```D
int parent() {
   myControlFlowStmt(args) { lambda content; return 0; }
}
```
is lowered to
```D
int parent() {
   myControlFlowStmt(args, () { lambda content; parent.return 0; 
});
}
```
The lowering for `foreach` does something similar, but there 
isn't really a way to utilize that functionality outside of it.



If you have multiple nested functions, search without a leading 
dot is inside-out and outside-in with a leading dot:
```D
void parent() // 1
{
     void parent() // 2
     {
         void parent() // 3
         {
             void child()
             {
                 return; // exits child
                 child.return; // (same) exits child
                 parent.return; // exits 3
                 parent.parent.return; // exits 2
                 parent.parent.parent.return; // exits 1
                 .parent.return; // exits 1
                 .parent.parent.return; // exits 2
                 .parent.parent.parent.return; // exits 3
                 .child.return; // error (child is not at global 
scope)
                 .parent.child.return; // error (child not 
directly under global .parent)
             }
         }
     }
}
```
If you name the parents differently, of course you don't need all 
these; `parent2.return` will suffice. Also note that nesting 
functions with the same name is legal in current D.


This looks like it asks for spaghetti code. But it's rather for 
being able to express something one cannot otherwise (easily) do. 
You would want that if you’re caught between code duplication and 
optimization.
Mar 14 2022
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Mar 14, 2022 at 05:33:29PM +0000, Quirin Schroll via Digitalmars-d
wrote:

 Imagine you have a loop that does multiple things slightly
 differently. You think about refactoring those similar parts into a
 single function that takes a few parameters and could even have a
 nice, descriplive name. And you can’t — because there's a pesky
 `return` or `break` statement that just wouldn't work when put into
 another function. How would it even work anyways? Well, how would it?
 It couldn’t ever work in a global-scope function, that’s for sure, but
 it could — in principle — work in a nested function or a lambda
under
 some reasonable conditions.
[...] This is a similar problem to the one already solved by opApply: have the function return a value that indicates whether the containing loop should continue executing or not. For example, when iterating over an aggregate containing .opApply, the loop body is transformed such that `continue;` becomes `return 0;` and `break;` becomes `return 1;`. The loop implementation stops the loop if the delegate returns non-zero. In a similar vein your refactored code could just replace a hoisted code block with: if (refactoredCode(...)) break; Depending on the circumstances you may need to further differentiate between normal return and short-circuit `continue`, so you use a scheme where 0 = normal return, 1 = break, -1 = continue. So you'd do: L1: foreach (...) { ... switch (auto ret = refactoredCode(...)) { case 1: break L1; case -1: continue; } ... } Kinda verbose, I'll grant you that, but I'm sure you can think of ways of making it more concise. T -- I think Debian's doing something wrong, `apt-get install pesticide', doesn't seem to remove the bugs on my system! -- Mike Dresser
Mar 14 2022
prev sibling parent Elronnd <elronnd elronnd.net> writes:
On Monday, 14 March 2022 at 17:33:29 UTC, Quirin Schroll wrote:
 *snip*
Common lisp does this, if you are looking for precedent.
Mar 14 2022