I created a starting set of NV, NVI and NVD macros similar (but slightly
different) to what mg has described previously. I see that as a starting
point for discussion.

Something like 'returnIf' wouldn't be hard to add but I'd personally prefer
to explore enhancing our switch statement first since I think that would
cover many use cases where I'd be tempted to try 'returnIf'.

Just on switch statements we have Java 13/14 enhanced switch we could
explore (switch expressions, yields/no breaks) and destructuring like
python's latest proposal[1] but obviously with our own syntax.


Cheers, Paul.
[1] https://www.python.org/dev/peps/pep-0622/

On Wed, Aug 5, 2020 at 7:53 AM MG <mg...@arscreat.com> wrote:

> Hi Eric,
>
> yea,  I got that, that's why I said "In that case a global setting might
> *also* be useful".
>
> But I doubt that the majority of Groovy users out there who want to
> quickly check if it is macros that make their code break in Groovy 4 would
> know that to do so they just need to "add the macro transform class to the
> disallowed list in CompilerConfiguration";  to be able to do so would mean
> one would a) need to know the macro transform class exists and what its
> purpose and exact name is, b) how the disallowed list in
> CompilerConfiguration works (that's the easy part), as well as last but not
> least c) to be sure that doing so will not just break part or all of
> Groovy... ;-)
>
> I think you grossly underestimate the amount of Groovy (internal)
> knowledge you have :-)
>
> Cheers,
> mg
>
>
> On 04/08/2020 18:27, Milles, Eric (TR Technology) wrote:
>
> In terms of globally disabling macro methods, you can just add the macro
> transform class to the disallowed list in CompilerConfiguration.  I think
> Paul is describing a mechanism where an individual macro method is taken
> out of service.
>
>
>
> *From:* MG <mg...@arscreat.com> <mg...@arscreat.com>
> *Sent:* Tuesday, August 4, 2020 9:53 AM
> *To:* dev@groovy.apache.org; Paul King <pa...@asert.com.au>
> <pa...@asert.com.au>
> *Subject:* Re: [PROPOSAL]Support conditional return
>
>
>
> Hi Paul,
>
> thanks for clearing that up :-)
>
> @unforeseen implications: In that case a global
> -Dgroovy.macro.enable=false
> might also be useful, to do a quick check if it is macros that are causing
> the problem (if we do not have that already).
>
> Btw: Do we have a way to hide the macro definitions from e.g. IntelliJ
> Intellisense, and only show the stub implementation ? I use the NV macros*
> extensively by now in my code, and what I found is, that always having to
> select and import the stub class, and not the macro class is a (small)
> hassle.
>
> Cheers,
> mg
>
> *In practice it turns out the NV variety I use the most is NVL, which
> returns a list of NV instances, so is good for logging multiple variables.
> At some point in the future there will need to be a discussion what
> varieties we want to support; my suggestion would be:
> NV(x) ... single NameAndValue class instance
> NVL(x0,x1,...) ... list of NameAndValue instances
> NVS(x0,x1,...) ... "x0=$x0, x1=$x1, ..."-GString
> (we could also have NVS return an (efficiently built) String, and NVGS
> return the GString, but I am not sure whether that it is worth it)
>
> On 04/08/2020 08:17, Paul King wrote:
>
>
>
> Hi mg,
>
>
>
> Just on supplying our own macros, we should do this for Groovy 4. We have
> been reluctant so far because we have been conservative about unforeseen
> implications. However, unless we start using them more widely, we aren't
> going to learn those implications.
>
>
>
> I'd suggest having them (to start with) in their own optional incubating
> module (e.g. groovy-macro-samples) and we should come up with a way to
> disable any one of them, e.g.
> -Dgroovy.macro.enable.returnIf=false  -Dgroovy.macro.enable.NV=true (or
> whatever).
>
>
>
> Cheers, Paul.
>
>
>
> On Wed, Jul 29, 2020 at 10:07 AM MG <mg...@arscreat.com> wrote:
>
> I like that idea :-)
> (unless someone has a really convincing argument why not, 100% for
> sticking with "it" instead of "_"/"$")
>
> That would also allow for more flexibility with e.g. regards to the
> number of methods that are being evaluated, without getting into the
> problematic area of whether/ how to support this syntax-wise.
>
> If there is nothing blocking this, the question is if Groovy should
> supply a basic version of such a macro (if Groovy is ever planning to
> supply macros of its own) ?
>
> Cheers,
> mg
>
>
> On 28/07/2020 16:08, Milles, Eric (TR Technology) 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&amp;data=02%7C01%7Ceric.milles%40thomsonreuters.com%7C411c66fda05844d7429908d831bacc9d%7C62ccb8646a1a4b5d8e1c397dec1a8258%7C0%7C0%7C637314025668554080&amp;sdata=vNa3dz0H%2BJAegS9Zb8HW2by0ueceqCKI6qDVFpBpbc4%3D&amp;reserved=0
> <https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Ftwitter.com%2Fbsideup%2Fstatus%2F1287477595643289601%3Fs%3D20&data=02%7C01%7Ceric.milles%40thomsonreuters.com%7Cd22419dbc45b40d96bf608d8388618f6%7C62ccb8646a1a4b5d8e1c397dec1a8258%7C0%7C0%7C637321496617678018&sdata=NBAn2FtRigVd8UFrZpPt7GdDLdI7kMVh%2BxTl2vNAtK0%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&amp;data=02%7C01%7Ceric.milles%40thomsonreuters.com%7C411c66fda05844d7429908d831bacc9d%7C62ccb8646a1a4b5d8e1c397dec1a8258%7C0%7C0%7C637314025668554080&amp;sdata=5m%2B5ejCWEicseaUp5wK0UDjHwpfMFht5ptjglZ9IWS4%3D&amp;reserved=0
> <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%7Cd22419dbc45b40d96bf608d8388618f6%7C62ccb8646a1a4b5d8e1c397dec1a8258%7C0%7C0%7C637321496617688010&sdata=vHsd4PrZTGJxhfAvtPxKLEfAvxiidhOAVvqFthIDHTU%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
> <https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%252>
> >>>>> Fmedium.com%2Fncr-edinburgh%2Fearly-exit-c86d5f0698ba&amp;data=02
> >>>>> %7C01%7Ceric.milles%40thomsonreuters.com
> <https://nam02.safelinks.protection.outlook.com/?url=http%3A%2F%2F40thomsonreuters.com%2F&data=02%7C01%7Ceric.milles%40thomsonreuters.com%7Cd22419dbc45b40d96bf608d8388618f6%7C62ccb8646a1a4b5d8e1c397dec1a8258%7C0%7C0%7C637321496617688010&sdata=dVEeA8WhNRImv11c7OIRUDcIyrjq58kF0o%2F5R%2BK6WlU%3D&reserved=0>
> %7C411c66fda05844d7429908
> >>>>> d831bacc9d%7C62ccb8646a1a4b5d8e1c397dec1a8258%7C0%7C0%7C637314025
> >>>>> 668554080&amp;sdata=q8VrgoQDeH85232oyMgQT8WwljNqoUjIc4cS7GGqH5I%3
> >>>>> D&amp;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