Hi mg, I like your idea, but it's hard for IDE to infer the type of `it` during we coding.
``` returnIf(a > 6 && it > 10) { goo() } ``` Cheers, Daniel Sun On 2020/07/29 20:28:13, MG <mg...@arscreat.com> wrote: > 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 > >>>>>>>> > >>>> > >