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 <mailto: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 <mailto:sun...@apache.org>>
    > Sent: Sunday, July 26, 2020 6:23 PM
    > To: dev@groovy.apache.org <mailto: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
    )
    >> 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
    <mailto: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
    <mailto: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
    <mailto: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).
    >>>>> 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&amp;data=02
    >>>>> %7C01%7Ceric.milles%40thomsonreuters.com
    <http://40thomsonreuters.com>%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
    <mailto: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 <mailto: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