Alternative solutions are coming in hard & fast ;-)
Applied to this specific problem, while clever, I feel that this suggestion would not express as clearly what is actually happening :-)

Apart from that I would however like to have the ability to return from the/an enclosing method inside a closure.

The problem for me here is, how does Groovy know what "doSomething" is, since it only makes sense if there actually is an enclosing doSomething method around the closure. This stems from the fact that what we actually want here is in fact not a closure, but a "closure-like custom block construct" ("clcbc")

If we had a way to tell Groovy to treat the curly braces { ... } term after the "tap" as a block instead of a closure, then a regular return would just do what we want, without any additional syntax, and (assuming "tap" would "auto-blockify" its single closure argument, as long as it was a closure literal) we could hypothetically write:

def doSomething(int a) {
   callB().tap { if (a > 6 && it > 10) return it }  // literal tap closure argument gets inlined => return statement returns from doSomething method
   callC().tap { if ( a > 5 && it > 20) return it } // same
   final tapCls = { result -> if ( a > 4 && result > 30) return result }
   callD().tap(tapCls) // this would not work as expected, tapCls would not get inlined, and "return result" would therfore only leave the tapCls
}

We have discussed this before, but if it is doable, I still feel there would be a lot of power in a feature like that... (not "inlining" for performance - which often times will not be beneficial and just bloat the compiled code - but to be able to have custom block constructs which automatically support break/return/continue).

Cheers,
mg


On 30/07/2020 00:19, Leonard Brünings wrote:
Hi,

just to throw it out there. As far as I understand this, we want to be
able to return based on the value of computed by a function without
having to assign the value to a variable first.

Another potentially more powerful alternative would be if we could
return the method from inside a closure.

def doSomething(int a) {
    callB().tap { if (a > 6 && it > 10) return@doSomething it }
    callC().tap { if ( a > 5 && it > 20) return@doSomething it }
    callD().tap { if ( a > 4 && it > 30) return@doSomething it }
}

While it may not be as concise as the original proposal, it fits better
in the existing syntax IMO.

The exact syntax of return@doSomething is up for debate, i'd imagine it
similar to break labels, with the method name being an implicit label.
Alternatively, we could use a special keyword for it.

On the discussion about "_", "$", or "it" I would strongly prefer "it"
as it would be consitent with other cases.


On a side note MG mentioned:

(if we had "??=" as "assign iff RHS != null", "??:" for "?: with
non-nullness", and could throw where an expression was expected)

JavaScript just got a new set of conditional assignment operators
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators

Name                                       Shorthand operator     Meaning

Logical AND assignment       x &&= y                          x && (x = y)
Logical OR assignment          x ||= y                            x ||
(x = y)
Logical nullish assignment    x ??= y                           x ?? (x = y)

Regardless of the outcome of this proposal, it might be worthwhile to
add them to groovy.

Cheers

Leo


Am 29.07.2020 um 22:28 schrieb MG:
Hi Daniel,

good idea also :-)

If the arguments are in this order however, it looks like a regular if
with its execution block to me...
Having the method call as the second argument would express more
intuitively what is happening - so maybe one should flip the order of
the arguments, and use:

returnIf(a > 6 && it > 10) { goo() }

Cheers,
mg


PS: If it = callB() were to be executed lazily, and no "it" argument
existed inside the condition, it would not be evaluated at all if
condition evaluates to false, making

returnIf(<condition>) { goo() }

equivalent to

if(<condition>) { return goo() }



On 29/07/2020 05:18, Daniel Sun wrote:
Hi Eric,

     I like your idea too ;-)

     We could use closure to express the condition at the tailing of
method call:
```
def doSomething(int a) {
    returnIf(callB()) { a > 6 && it > 10 }
    returnIf(callC()) { a > 5 && it > 20 }
    returnIf(callD()) { a > 4 && it > 30 }
}
```

Cheers,
Daniel Sun
On 2020/07/28 14:08:45, "Milles, Eric (TR Technology)"
<eric.mil...@thomsonreuters.com> wrote:
If switch expression or pattern match macro is insufficient, could a
macro be written to cover this "conditional return"?

// "it" could easily be replaced by "_" or "$" as mentioned
previously as options
def doSomething(int a) {
    returnIf(callB(), a > 6 && it > 10)
    returnIf(callC(), a > 5 && it > 20)
    returnIf(callD(), a > 4 && it > 30)
}

    vs.

def doSomething(int a) {
    return callB() if (a > 6 && _ > 10)
    return callC() if (a > 5 && _ > 20)
    return callD() if (a > 4 && _ > 30)
}

-----Original Message-----
From: Daniel Sun <sun...@apache.org>
Sent: Sunday, July 26, 2020 6:23 PM
To: dev@groovy.apache.org
Subject: Re: [PROPOSAL]Support conditional return

Hi Sergei,

( Copied from twitter:
https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Ftwitter.com%2Fbsideup%2Fstatus%2F1287477595643289601%3Fs%3D20&data=02%7C01%7Ceric.milles%40thomsonreuters.com%7C411c66fda05844d7429908d831bacc9d%7C62ccb8646a1a4b5d8e1c397dec1a8258%7C0%7C0%7C637314025668554080&sdata=vNa3dz0H%2BJAegS9Zb8HW2by0ueceqCKI6qDVFpBpbc4%3D&reserved=0
)
But isn't it better with pattern matching? And what is "_" here?
The underscore represents the return value

Anyways:
```
return match (_) {
      case { it < 5 }: callC();
      case { it > 10 }: callB();
      case { it != null }: callA();
      default: {
          LOG.debug "returning callD"
          return callD()
      }
}
```
pattern matching may cover some cases of Conditional Return, but it
can not cover all. Actually the Conditional Return is more flexible,
e.g.

```
def chooseMethod(String methodName, Object[] arguments)  {
     return doChooseMethod(methodName, arguments) if _ != null

     for (Class type : [Character.TYPE, Integer.TYPE]) {
        return doChooseMethod(methodName,
adjustArguments(arguments.clone(), type)) if _ != null
     }

     throw new GroovyRuntimeException("$methodName not found") } ```

Even we could simplify the above code with `return?` if the
condition is Groovy truth:
```
def chooseMethod(String methodName, Object[] arguments)  {
     return? doChooseMethod(methodName, arguments)

     for (Class type : [Character.TYPE, Integer.TYPE]) {
        return? doChooseMethod(methodName,
adjustArguments(arguments.clone(), type))
     }

     throw new GroovyRuntimeException("$methodName not found") } ```

Cheers,
Daniel Sun
On 2020/07/26 18:23:41, Daniel Sun <sun...@apache.org> wrote:
Hi mg,

maybe you can give some real life code where you encounter this on
a regular basis ?
Let's think about the case about choosing method by method name and
arguments:

```
def chooseMethod(String methodName, Object[] arguments) {
     def methodChosen = doChooseMethod(methodName, arguments)
     if (null != methodChosen) return methodChosen

     methodChosen = doChooseMethod(methodName,
adjustArguments(arguments.clone(), Character.TYPE))
     if (null != methodChosen) return methodChosen

     methodChosen = doChooseMethod(methodName,
adjustArguments(arguments.clone(), Integer.TYPE))
     if (null != methodChosen) return methodChosen

     throw new GroovyRuntimeException("$methodName not found") } ```

The above code could be simplified as:
```
def chooseMethod(String methodName, Object[] arguments) {
     return? doChooseMethod(methodName, arguments)

     return? doChooseMethod(methodName,
adjustArguments(arguments.clone(), Character.TYPE))

     return? doChooseMethod(methodName,
adjustArguments(arguments.clone(), Integer.TYPE))

     throw new GroovyRuntimeException("$methodName not found") } ```

Or a general version:
```
def chooseMethod(String methodName, Object[] arguments) {
     return doChooseMethod(methodName, arguments) if _ != null

     return doChooseMethod(methodName,
adjustArguments(arguments.clone(), Character.TYPE)) if _ != null

     return doChooseMethod(methodName,
adjustArguments(arguments.clone(), Integer.TYPE)) if _ != null

     throw new GroovyRuntimeException("$methodName not found") } ```


Cheers,
Daniel Sun
On 2020/07/26 17:11:07, MG <mg...@arscreat.com> wrote:
Hi Daniel,

currently I would be +/- 0 on this.

Thoughts:

   1. I feel I have written this before, but I myself do not
encounter the
      situation where I would need to return the result of a method
call
      only if it meets certain conditions when programming (maybe
you can
      give some real life code where you encounter this on a
regular basis ?).
   2. If I have more than one return, it typcially is an early out,
which
      depends on the method's input parameters, not on the result of
      another method call.
   3. Since I do a lot of logging / log debugging, I typically
assign the
      return value to a variable, so I can debug-log it before the one
      return of the method.
   4. In fact I have had to refactor code written by other people from
      multi-return methods to single return, to be able to track
down bugs.

So overall I am not sure one should enable people to make it easier
to write non-single-return methods ;-)


Purely syntax wise I would prefer
return?
for the simple case,

and

return <something> if <condition>
for the more complex one*.

I find
return(<condition)  <something>
confusing on what is actually returned.

Cheers,
mg

*Though I wonder if people would not then expect this
if-postfix-syntax to also work for e.g. assignments and method
calls...


On 26/07/2020 16:15, Daniel Sun wrote:
Hi Mario,

       I think you have got the point of the proposal ;-)

       If we prefer the verbose but clear syntax, I think we could
introduce `_` to represent the return value for concise shape:

```
return callB() if (_ != null && _ > 10)

// The following code is like lambda expression, which is a bit
more verbose return callB() if (result -> result != null && result
10) ```
       Show the `_` usage in your example:
```
def doSomething(int a) {
     return callB() if (a > 6 && _ > 10)
     return callC() if (a > 5 && _ > 20)
     return callD() if (a > 4 && _ > 30) } ```

```
// optional parentheses
def doSomething(int a) {
     return callB() if a > 6 && _ > 10
     return callC() if a > 5 && _ > 20
     return callD() if a > 4 && _ > 30 } ```

```
// one more example
def doSomething(int a) {
     return callB()                if a > 6 && _ > 10
     return callC() + callD() if a > 5 && _ > 50 } ```

       BTW, the parentheses behind `if` could be optional.

Cheers,
Daniel Sun
On 2020/07/26 11:29:39, Mario Garcia <mario.g...@gmail.com> wrote:
Hi all:

Very interesting topic.

The first idea sprang to mind was the PMD rule in Java saying you
should have more than one exit point in your methods (
https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpmd.github.io%2Flatest%2Fpmd_rules_java_codestyle.html%23onlyonereturn&data=02%7C01%7Ceric.milles%40thomsonreuters.com%7C411c66fda05844d7429908d831bacc9d%7C62ccb8646a1a4b5d8e1c397dec1a8258%7C0%7C0%7C637314025668554080&sdata=5m%2B5ejCWEicseaUp5wK0UDjHwpfMFht5ptjglZ9IWS4%3D&reserved=0).

But the reality is that sometimes (more often than not) we are
forced to break that rule. In fact sometimes we could even argue
that breaking that rule makes the code clearer (e.g
https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2
Fmedium.com%2Fncr-edinburgh%2Fearly-exit-c86d5f0698ba&data=02
%7C01%7Ceric.milles%40thomsonreuters.com%7C411c66fda05844d7429908
d831bacc9d%7C62ccb8646a1a4b5d8e1c397dec1a8258%7C0%7C0%7C637314025
668554080&sdata=q8VrgoQDeH85232oyMgQT8WwljNqoUjIc4cS7GGqH5I%3
D&reserved=0)

Although my initial reaction was to be against the proposal,
however after doing some coding, I've found that neither elvis
nor ternary operators makes it easier nor clearer. Here's why I
think so. Taking Daniel's example:

```
def m() {
      def a = callA()
      if (null != a) return a

      def b = callB()
      if (b > 10) return b

      def c = callC()
      if (null != c && c < 10) return c

      LOGGER.debug('the default value will be returned')

      return defaultValue
}
```
The shortest elvis operator approach I could think of was:
```
def m2() {
      return callA()
          ?: callB().with { it > 10 ? it : null }
          ?: callC().with { null != it && it <10 ? it : null } }
```

which to be honest, is ugly to read, whereas Daniel's proposal
is just:

```
def m() {
      return? callA()
      return(r -> r > 10) callB()
      return(r -> null != r && r < 10) callC()
      return defaultValue
}
```

Once said that, I would say this conditional return could be
useful only when there are more than two exit points, otherwise
ternary or elvis operators may be good enough.

So, bottom line, I kinda agree to add conditional return, but I'm
not sure about the final syntax:

```
return(r -> r > 10) callB()
return callB() [r -> r > 10]
return callB() if (r -> r > 10)
```

Between the three I the one that I like the most is the third one:

```
return callB() if (r -> r > 10)
```

You can read it in plain english as "return this if this
condition happens".

Apart from Daniel's use case, using this option could open the
possibility to use, not only a closure or lambda expression, but
also a plain expression. A nice side effect could be that
something like the following code:

```
def doSomething(int a) {
     return callB() if a > 6
     return callC() if a > 5
     return callD() if a > 4
}
```

turns out to be a shorter (and in my opinion nicest) way of
switch case (when you want every branch to return something):

```
def doSomething(int a) {
     switch (a) {
        case { it > 6 }: return callB()
        case { it > 5 }: return callC()
        case { it > 4 }: return callD()
     }
}
```

Well, bottom line, I'm +1 Daniel's proposal because I've seen
some cases where this conditional return could make the code
clearer.

Cheers
Mario

El sáb., 25 jul. 2020 a las 23:55, Paolo Di Tommaso (<
paolo.ditomm...@gmail.com>) escribió:

It's not much easier a conditional expression (or even the elvis
operator)?

```
def m() {
       def r = callSomeMethod()
       return r != null ? r : theDefaultResult } ```


On Sat, Jul 25, 2020 at 8:56 PM Daniel Sun <sun...@apache.org>
wrote:

Hi all,

        We always have to check the returning value, if it match
some condition, return it. How about simplifying it? Let's see
an example:

```
def m() {
       def r = callSomeMethod()
       if (null != r) return r

       return theDefaultResult
}
```

How about simplifying the above code as follows:
```
def m() {
       return? callSomeMethod()
       return theDefaultResult
}
```

Futhermore, we could make the conditional return more general:
```
def m() {
       return(r -> r != null) callSomeMethod() // we could do
more checking, e.g. r > 10
       return theDefaultResult
}
```

       Any thoughts?

Cheers,
Daniel Sun


Reply via email to