3

Sorry if this question is duplicate. But I can't find the difference in usage. When I run the below codes I am getting different answers. I see from most tutorials that using of "do ... end" is same as "{ ... }" blocks.

include Comparable

a = [1, 4, 2, 3, 5]

p a.sort do |x,y|
    y <=> x
end

output shows as = [1, 2, 3, 4, 5]

but when I run like this...

include Comparable

a = [1, 4, 2, 3, 5]

p a.sort { |x,y|
    y <=> x
}

output shows as = [5, 4, 3, 2, 1]

what was wrong here. is there any situations that two syntaxes has any different behaviour?

rootatdarkstar
  • 1,486
  • 2
  • 15
  • 26
mmar
  • 1,840
  • 6
  • 28
  • 41

3 Answers3

4

The precedence is different. The first one is interpreted as

p (a.sort) do
  ...
end

Since the block is not passed to sort, it is sorted in the default, ascending order. Then, the block is passed to p, which does not use it.

sawa
  • 165,429
  • 45
  • 277
  • 381
  • So, do we have to use `{...}` ?. If so, in which situations should we use `do...end` and `{...}`? – mmar Jun 05 '16 at 03:42
  • 2
    You can use either. As Óscar wrote, the convention is to use `do…end` for multi-line and `{…}` on a single line. However, if you use `{…}` along with a methods that takes arguments you may need to wrap the arguments in parentheses to make it clear. – Phrogz Jun 05 '16 at 03:49
  • Now i understand that we can use both and which one for single line & which one for multiline. As everyone said both are correct, still not getting, why the output is showing different when i use `do...end`. Please help me to understand? Any detailed explanation would be much appreciatable? – mmar Jun 05 '16 at 03:54
2

As others have pointed out, there is a difference in precedence between the two types of blocks. However, the precedence does not really influence how it's used in practice.

Some Rubyists follow the Weirich Convention, named so by Avdi Grimm.

In short, the use of one over the other is based on whether it's functional (return value is used), in which case { ... } is used, or procedural (changes the state of the system in some way or performs output), in which case do ... end is used.

Robin Daugherty
  • 7,115
  • 4
  • 45
  • 59
  • That's a very controversial style. I'm with @Phrogz here. Single line uses the braces, multi-line uses `begin ... end` for clarity. Every style-guide and programmer is entitled to their opinion, obviously. I just find this particular style to be so quirky it's not very popular. – tadman Jun 05 '16 at 05:15
  • The problem with such convention is that the two are not mutually exclusive. Sometimes, a method can have side effects/outputs, and its return value may be used. – sawa Jun 05 '16 at 08:00
  • Following the convention, if the return value is used, it's a functional block, regardless of side effects. However I would suggest that a block with both side effects and a return value should be split up in some way to make the intent clearer. I think that's reasoning behind this convention: to make it easier to write with clear intent. As Avdi says, "It adds a visual cue about the intent of the code that wouldn’t otherwise be there." – Robin Daugherty Jun 06 '16 at 16:41
2

sawa's answer is correct, but as the OP has asked for more clarification, I'm supplying my own answer.

All four of these method calls behave the same, passing a block to a foo method:

foo { ... }
foo do ... end
foo() { ... }
foo() do ... end

When you write two methods and a block, without parentheses around the arguments, it is unclear which method the block goes with:

foo bar { ... }
foo bar do ... end

The question is: "Am I passing a block to bar, and then passing its return value to foo? Or am I calling foo with bar as an argument and also passing along a block?"

With parentheses, you can make this clear using either block style:

# Passing a block to `bar`, and then passing the result to `foo`
foo( bar { ... } )
foo( bar do ... end )

# Passing an argument and  block to `foo`
foo( bar ) { ... }
foo( bar ) do ... end

The difference between {…} and do…end that you have run into is where Ruby chooses to put the parentheses when you omit them. The two block notations have different precedence, and so you end with different results:

# Passing a block to `bar`, and then passing the result to `foo`
foo bar{ ... }
foo( bar do ... end )

# Passing an argument and  block to `foo`
foo bar do ... end
foo( bar ){ ... }

So, specifically in your case:

# This code…
p a.sort do |x,y|
    y <=> x
end

# …is the same as this code:
b = a.sort
p(b){ |x,y| y <=> x }

# Note that you are passing a block to the `p` method
# but it doesn't do anything with it. Thus, it is
# functionally equivalent to just:
p a.sort

And,

# This code…
p a.sort { |x,y|
    y <=> x
}

# …is functionally the same as this code:
b = a.sort{ |x,y| y <=> x }
p b

Finally, if you still don't get it, perhaps deeply considering the following code and output will help:

def foo(a=nil)
  yield :foo if block_given?
end

def bar
  yield :bar if block_given?
  :bar_result
end

foo bar { |m| puts "block sent to #{m}" }
#=> block sent to bar
#=> foo got :bar_result

foo( bar do |m| puts "block sent to #{m}" end )
#=> block sent to bar
#=> foo got :bar_result

foo( bar ){ |m| puts "block sent to #{m}" }
#=> foo got :bar_result
#=> block sent to foo

foo bar do |m| puts "block sent to #{m}" end
#=> foo got :bar_result
#=> block sent to foo

Notice that the first and last examples in the code immediately above differ only in whether they use {…} or do…end for the block.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
  • I got some idea when to use '{...}` and 'do...end` based on your example explained with my code rather than all the `foo....' examples. Thanks for your detailed explanation. Much appreciate your time. – mmar Jun 05 '16 at 17:11