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 > > > >>>> > > > > > > > > >