For context, here's a summary of a discussion we had outside this thread.

Like MG, I mostly use multiple returns for early bailouts of the method,
and only occasionally otherwise where it adds additional readability.  I
don't find the standard Java-like syntax for your use case
overly-cumbersome, i.e.
def _
if ((_ = doA()) != null) { return _ }
else if ((_ = doB()) > 10) { return _ }
else if ((_ = doC()) < 10) { return _ }
return defaultValue

I have three concerns about this proposal.  First, _ is already a valid
identifier, so it'd be a breaking change to change its meaning. Second, it
feels inconsistent to introduce a single way in which an if can be trailing
(in Kotlin I think this feels consistent because it can be used in both
assignments and returns).  Third, this seems like syntax to support a
rather specific use-case.  For example, what if I have more than one
variable involved in my multiple returns.  For example, how would this be
represented?
def a = callA()
def b = callB()
if (a > b && b > 1) return a
else if (b > a && a > 10) return b
return 0

This last example is what his latest post is in response to.

On that syntax, it would reuse the same syntax as labels on loops.  Let's
say the methods invoked have side-effect behavior on this.done.  I haven't
dug into the code, but would we be able to handle (or fail compilation for)
something like
returnA:
while (!this.done) {
  if (this.isSpecialCase() break returnA
  returnA:
  return callA() if _ > 5
  return callB() if _ > 10 && returnA._ > 1
}
return this.defaultValue

-Keegan

On Sun, Jul 26, 2020 at 1:11 PM 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> 
> <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://pmd.github.io/latest/pmd_rules_java_codestyle.html#onlyonereturn).
> 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.ghttps://medium.com/ncr-edinburgh/early-exit-c86d5f0698ba)
>
> 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> 
> <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