0

EDIT: I've received answers about refactoring to a ternary operation. While I'm aware that that's possible with all the languages below, that wasn't necessarily my intent with the original question about style. I've edited the snippets to reflect this.


I am curious if it's idiomatic or preferred to always use an else when using if/else in a method.

For example, in JS, this is stylistically acceptable (and sometimes preferred)

function jsFunc(x) {
  if (x == 0) {
    // do a bunch of stuff here
    return answer;
  }

  // do something else
  return differentAnswer;
}

and I've also seen a similar style in Python:

def py_func(x):
    if x == 0:
        # do a bunch of stuff here
        return answer
    # do something here
    return differentAnswer

but from the Ruby I've seen, it's always:

def ruby_method(x)
  if x == 0
    # do a bunch of stuff here
    answer
  else
    # do something else
    differentAnswer
  end
end

I've also gotten a comment that not using an else in Ruby seems messier. Do Rubyists prefer the explicit else? (Also curious a lot of the language is implicit.)

jtanadi
  • 3
  • 1
  • 2
  • 4
  • 2
    [Disclaimer: _very opinionated, but still true_] Rubyists prefer not to use `if/else` condition at all since it’s already bringing too much mess. – Aleksei Matiushkin Mar 29 '18 at 12:20
  • The example you provided is not good for the question because it can be written as `return x == 0 ? "Yes" : "No"` in many languages. You should add some code on both branches of the `if` statement to make the question relevant. – axiac Mar 29 '18 at 12:34
  • 2
    From Martin Fowler's Book [Refactoring: Improving the Design of Existing Code](https://martinfowler.com/books/refactoring.html) (there's also a [Ruby Edition](https://martinfowler.com/books/refactoringRubyEd.html)): _"The key point about Replace Nested Conditional with Guard Clauses is one of emphasis. If you are using an if-then-else construct you are giving equal weight to the if leg and the else leg. This communicates to the reader that the legs are equally likely and important. Instead the guard clause says, “This is rare, and if it happens, do something and get out.”"_ – Stefan Mar 29 '18 at 14:08
  • @Stefan Thank you. I think the confusing thing for me, then, is that in the JS and Py style / linter guides I've seen, there is no way to _"communicate to the reader that the legs are equally likely and important"_. The else returns are just seen as unnecessary ([JS](https://stackoverflow.com/questions/46875442/unnecessary-else-after-return-no-else-return), [Py](https://stackoverflow.com/a/28250521/9569550)). – jtanadi Mar 29 '18 at 14:23
  • The rules you are referring to (e.g. [`no-else-return`](https://eslint.org/docs/rules/no-else-return)) are about using `else` _after_ `return`, they are not about disallowing `else` in general. A simple and linter-friendly workaround therefore is to assign the result to a variable and return the variable after the `if-else` block. – Stefan Apr 04 '18 at 15:51

2 Answers2

6

It actually depends.

Guard clause returns are actually used in Ruby quite often. They are even encouraged by the widely accepted style guide.

However, guard clauses are used in corner cases. So you have to ask yourself what are the possible values of x and how often is it actually 0?


If it's rarely 0 and it's somewhat special, the return at the start is perfectly acceptable (and encouraged).

For example, this is a perfectly serviceable (even though not optimal) implementation of factorial:

def factorial(n)
  return 1 if n.zero?

  n * factorial(n.pred)
end

Reason being that 0 is definitely a corner case.


If, on the other hand, the 'Yes' and 'No' cases are both likely and normal, this symmetry should be visually represented in the code (if - else).

For example, consider a robot deciding what to do on a traffic light:

def take_action(light)
  if light.green?
    go
  elsif light.yellow?
    prepare_to_go
  elsif light.red?
    wait
  end
end

You can write it with guard clauses, but it sounds weird. All of the possible colours are equally a "main" colour.


Either way, in this specific case there is a more concise alternative:

x.zero? ? 'Yes' : 'No' 
ndnenkov
  • 35,425
  • 9
  • 72
  • 104
  • Thanks for the thorough answer. I think part of my curiosity also has to do with the fact that in the JS and py codes I've seen, the lack of an explicit else return is because they're seen as [unnecessary](https://eslint.org/docs/rules/no-else-return). To me that's still a little different than the use of guard clauses (at least the intentions seem different), even if the result may be the same, depending on the implementation. – jtanadi 5 mins ago – jtanadi Mar 29 '18 at 14:02
  • 1
    @jtanadi Ruby has an implicit return for all methods which skews things a bit here compared to others like JavaScript which do not. Ruby's `if` also returns the result of the branch executed. When combined these make for some very minimal code versus other languages doing the same thing. – tadman Mar 29 '18 at 16:36
1

I think this is due to the fact that in Ruby we have implicit return and Ruby developers tend to dislike the use of return. In fact, the alternative to your snippet would be

def ruby_method(x)
  return "Yes" if x.zero?
  "No"
end

Kinda strange I think

Ursus
  • 29,643
  • 3
  • 33
  • 50