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