63

What's the difference between these two Ruby if statements when we put a then at the end of the if statement?

if(val == "hi") then
  something.meth("hello")
else
  something.meth("right")
end

and

if(val == "hi")
  something.meth("hello")
else
  something.meth("right")
end
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Mo.
  • 40,243
  • 37
  • 86
  • 131

5 Answers5

77

then is a delimiter to help Ruby identify the condition and the true-part of the expression.

if condition then true-part else false-part end

then is optional unless you want to write an if expression in one line. For an if-else-end spanning multiple lines the newline acts as a delimiter to split the conditional from the true-part

# can't use newline as delimiter, need keywords
puts if (val == 1) then '1' else 'Not 1' end

# can use newline as delimiter
puts if (val == 1)
  '1'
else
  'Not 1'
end
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Gishu
  • 134,492
  • 47
  • 225
  • 308
  • 1
    In Ruby, `if` is an expression, not a statement. In fact, *everything* is an expression, there are no statements. Thus, both your examples would be better written like `puts if val == 1 then '1' else 'Not 1' end`. – Jörg W Mittag Jun 21 '10 at 11:31
  • 3
    @Jorg - right. It'll take some time to scrub the years of C off me. :) – Gishu Jun 21 '10 at 12:20
  • 1
    In ruby 2.1.2, `puts if (1 == 1) then '1' else 'not 1' end` raises an `syntax error, unexpected keyword_then, expecting end-of-input` unless you put the if statement in parentheses `puts (if (1 == 1) then '1' else 'not 1' end)`. – Sam Oct 12 '15 at 14:22
  • For completeness, you should favour the [ternary operator](https://github.com/bbatsov/ruby-style-guide#ternary-operator) for single line if expressions. `puts(1==1 ? '1' : 'Not 1')` – I_do_python Oct 26 '16 at 13:27
13

Here's a quick tip that is not directly related to your question: in Ruby, there is no such thing as an if statement. In fact, in Ruby, there are no statements at all. Everything is an expression. An if expression returns the value of the last expression that was evaluated in the branch that was taken.

So, there is no need to write

if condition
  foo(something)
else
  foo(something_else)
end

This would better be written as

foo(
  if condition
    something
  else
    something_else
  end
)

Or as a one-liner

foo(if condition then something else something_else end)

In your example:

something.meth(if val == 'hi' then 'hello' else 'right' end)

Note: Ruby also has a ternary operator (condition ? then_branch : else_branch) but that is completely unnecessary and should be avoided. The only reason why the ternary operator is needed in languages like C is because in C if is a statement and thus cannot return a value. You need the ternary operator, because it is an expression and is the only way to return a value from a conditional. But in Ruby, if is already an expression, so there is really no need for a ternary operator.

Ashitaka
  • 19,028
  • 6
  • 54
  • 69
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 7
    In Ruby there are many ways to reach the same goal. I personally like the ternary operator. I find it compact and readable :) – nathanvda Jun 21 '10 at 13:25
  • 3
    We use ruby extensively in our build system, I've found that the first syntax is easier to understand for developers coming from other languages. – Justin Ohms Jul 21 '15 at 20:41
  • 3
    Readability is paramount to quality code. Nesting a multi-line branching construct (statement or expression, regardless) inside a parameter list is absurd. It requires to much scanning and consideration to even get a high-level idea of what is going on. Spend a few extra keystrokes and lose a whisp of DRY to make your code more readable and supportable. Sneaking in a (simple) ternary operator does not violate that readability and would not get rejected in a pull request. – David Hempy Jul 19 '19 at 22:27
8

The then is only required if you want to write the if expression on one line:

if val == "hi" then something.meth("hello")
else something.meth("right")
end

That brackets in your example are not significant, you can skip them in either case.

See the Pickaxe Book for details.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Douglas
  • 36,802
  • 9
  • 76
  • 89
7

The only time that I like to use then on a multi-line if/else (yes, I know it's not required) is when there are multiple conditions for the if, like so:

if some_thing? ||
  (some_other_thing? && this_thing_too?) ||
  or_even_this_thing_right_here?
then
  some_record.do_something_awesome!
end

I find it to be much more readable than either of these (completely valid) options:

if some_thing? || (some_other_thing? && this_thing_too?) || or_even_this_thing_right_here?
  some_record.do_something_awesome!
end

# or

if some_thing? ||
  (some_other_thing? && this_thing_too?) ||
  or_even_this_thing_right_here?
  some_record.do_something_awesome!
end

Because it provides a visual delineation between the condition(s) of the if and the block to execute if the condition(s) evaluates to true.

jeffdill2
  • 3,968
  • 2
  • 30
  • 47
4

There's no difference at all.

And, just FYI, your code can be optimized to

something.meth( val == 'hi' ? 'hello' : 'right' )
zed_0xff
  • 32,417
  • 7
  • 53
  • 72