Hi OC,
regarding return statements working "as expected" inside loop/if
statement body like Closures:
1. As I said I already proposed Groovy support this here a few years back.
2. It never came to fruition, with all of the problems (besides someone
needing to do the actual coding) revolving around the simple fact
that Groovy uses Closures, not actual code blocks for all of its
non-Java style, stream-like loop/filter/collect/... support.
3. We have been using the ReturnException concept you mention in your
mail since 2016 in our code to support breaking from e.g. our own
SQL-Executor class loop constructs
1. e.g.:
* sqe.each("select $t.NICKNAME, $t.HEIGHT, $t.INFO from $p
where ${p.ID.isInSql(candidateIds)}") { GroovyRowResult row ->
o if(p.HEIGHT.getNumber(row) >
minimalHeightDependingOnName(p.NICKNAME.getString(row))
&& scanClobForRelevantTraits(p.INFO .getClob(row))) {
+ GroovyLoopHelper.breakFromLoop() // found linebacker
o }
* }
2. I have listed the code we use below, the basic principle is just
that we use Throwable|s, not Exception|s (so it passes by most
try-catch blocks), and we require all of our code that catches
Throwable|s to silently rethrow if the caught type extends
GroovyMandatorySilentlyRethrowThrowable.
3. And, yes, using try-catch here is of course "slow", but that is
irrelevant in any but the most time sensitive application.
1. It certainly does not matter when you are e.g. processing
the result of a database query that has been sent over a
network connection ;-)
4. We use return for loop-continue (ugly).
5. We currently don't support a return statement like construct.
4. I think to break the deadlock with the "but what if you pass around
the closure ?" argument Groovy would need to introduce something
that differentiates closures that are supposed to simulate block
constructs, by e.g. marking them as "block closures", or using a
BlockClosure subclass.
1. These block closures would need to be treated in a way by
Groovy, that ensures that e.g. a return statement inside them
actually returns from the enclosing method.
2. /And here comes the kicker:/ Since that cannot be done by using
actual closures unless one uses exceptions, which we cannot use
as a general approach, since they are "slow", that would only
work if we supported inlining of methods/closures !
3. Inlining of methods and closures is of course not trivial, but
more importantly some people here have in the past expressed
concerns that it is actually a dangerous thing to support, that
people could over- or abuse.
4. And from my memory these are the reasons this never moved forward...
Cheers,
mg
class GroovyMandatorySilentlyRethrowThrowable extends Throwable {}
class BreakFromGroovyLoopThrowable extends
GroovyMandatorySilentlyRethrowThrowable {
@Lazy static final /BREAK_FROM_GROOVY_LOOP /= new
BreakFromGroovyLoopThrowable()
}
class GroovyLoopHelper {
static void breakFromLoop() {
//throw BreakFromGroovyLoopThrowable./BREAK_FROM_GROOVY_LOOP
/}
static void handleGroovyMandatorySilentlyRethrowThrowable(Throwable
throwable) {
if(throwable instanceof GroovyMandatorySilentlyRethrowThrowable) {
throw throwable
}
}
static void each(iterable, Closure closure) {
try {
iterable.each(closure)
}
catch(BreakFromGroovyLoopThrowable ignored) {}
}
static void eachWithIndex(iterable, Closure closure) {
try {
iterable.eachWithIndex(closure)
}
catch(BreakFromGroovyLoopThrowable ignored) {}
}
}
/// Example of code that supports breakFromLoop: /
try {
/// do dangerous stuff that might throw Throwable|s .../
}
catch(Throwable throwable) {
GroovyLoopHelper./handleGroovyMandatorySilentlyRethrowThrowable/(throwable)
/// handle GroovyMandatorySilentlyRethrowThrowable /
/// Regular Throwable handling/
}
On 04/12/2024 18:03, OCsite wrote:
MG,
On 4. 12. 2024, at 16:11, MG <mg...@arscreat.com> wrote:
1. e.g. using a for(foo : foos) { ... } loop instead of
canonical Groovy foos.each { foo -> ... }, to be able to
easily return from the for body from multiple places using
return statements.
For one, I would argue that the native and groovier (since more
logical and intuitive and intention-revealing for anyone who can read
English completely regardless whether he knows Java or not) variant
should be the /for/in/ loop, like /for (foo *in* foos)/. That weird
and unintuitive colon thing should, in my personal opinion, remain
limited to code copy/pasted from Java (exactly like /var/ :))
That would not be worth an extra email though. I wonder, would it be
perhaps worth the effort to extend the language by adding a support
for method-returning from a closure?
A trivial (and most probably very very wrong and problems-inducing!)
approach might perhaps be an ASTT which would convert code like
def foo() {
bar.each { if (it) methodreturn it }
}
to something like
def foo() {
try {
bar.each { if (it) throw new MethodReturnException(value:it) }
} catch (MethodReturnException e) {
return e.value
}
}
Would it be perhaps worth the effort to add such an ASTT to Groovy?
Not sure at all... but it might help (a) to stick with the canonical
/foo.each/ instead of enforcing /for/ins/, (b) also, in many cases
like /foo.find/, /foo.allResults/, et cetera, which are even more ugly
to replace with plain ole loops.
All the best,
OC