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