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

Reply via email to