Using do block vs braces {}
Ruby cookbook says bracket syntax has higher precedence order than do..end
Keep in mind that the bracket syntax
has a higher precedence than the
do..end syntax. Consider the following
two snippets of code:
1.upto 3 do |x|
puts x
end
1.upto 3 { |x| puts x }
# SyntaxError: compile error
Second example only works when parentheses is used, 1.upto(3) { |x| puts x }
do..end vs curly braces for blocks in Ruby
The general convention is to use do..end for multi-line blocks and curly braces for single line blocks, but there is also a difference between the two that can be illustrated with this example:
puts [1,2,3].map{ |k| k+1 }
2
3
4
=> nil
puts [1,2,3].map do |k| k+1; end
#<Enumerator:0x0000010a06d140>
=> nil
This means that {} has a higher precedence than do..end, so keep that in mind when deciding what you want to use.
One more example to keep in mind while you develop your preferences.
The following code:
task :rake => pre_rake_task do
something
end
really means:
task(:rake => pre_rake_task){ something }
And this code:
task :rake => pre_rake_task {
something
}
really means:
task :rake => (pre_rake_task { something })
So to get the actual definition that you want, with curly braces, you must do:
task(:rake => pre_rake_task) {
something
}
Maybe using braces for parameters is something you want to do anyways, but if you don't it's probably best to use do..end in these cases to avoid this confusion.
differing `puts` behavior inside `do..end` blocks and curly braces in Ruby
do...end
and {}
are 100% semantically equivalent for method blocks. Their only difference is their parsing precedence, so they evaluate differently
To really understand that difference, a few things first.
Ruby lets you call methods without parens:
my_object.my_method my_arg
# so my_arg could actually be a method! Let's put parens in to show that:
my_object.my_method(my_arg())
Blocks in Ruby are method arguments -- a syntax for passing in closures (except for the special keywords that act in the parent scope). The below two chunks are equivalent:
[1, 2, 3].map { |x| 2 * x }
# split out into two lines
double = ->(x) { 2 * x } # shorthand for `lambda { |x| 2 * x }`
[1, 2, 3].map(&double)
Okay, so knowing all that, let's expose the difference between {}
and do...end
:
my_method [1, 2, 3].map { |x| 2 * x }
my_method([1, 2, 3].map { |x| 2 * x }) # parses like this
my_method [1, 2, 3].map do |x| 2 * x end
my_method([1, 2, 3].map) do |x| 2 * x end # parses like this
my_method([1, 2, 3].map) { |x| 2 * x } # in other words
{}
has more precedence than do...end
, immediately getting associated to the method immediately to its left. do...end
has lower precedence, and will associate with my_method
, which gets passed [1, 2, 3].map
and the block as arguments.
This means, what you did above is:
puts(my_array.each) { |num| num *= 2; puts "The new number is #{num}." }
You've passed into puts
the my_array.each
, which is an enumerator, and a block, and puts
does nothing with the blocks passed into it, as do all methods by default.
Ruby do/end vs braces
That's because the second line is interpreted as:
p(a.map) do ... end
instead of:
p(a.map do ... end)
The grammar is ambiguous in this case and do
doesn't seem to bind as strongly as {
.
Does Ruby read do and end the same way as { and }?
No, it doesn't. As a general rule of Ruby, if two things look alike, you can bet that there is a subtle difference between them, which makes each of them unique and necessary.
{
and }
do not always stand in the role of block delimiters. When they do not stand in the role of block delimiters (such as when constructing hashes, { a: 1, b: 2 }
), they cannot be replaced by do
... end
. But when the curly braces do delimit a block, they can almost always be replaced by do
... end
. But beware, because sometimes this can change the meaning of your statement. This is because {
... }
have higher precedence, they bind tighter than do
... end
:
puts [ 1, 2, 3 ].map { |e| e + 1 } # { ... } bind with #map method
2
3
4
#=> nil
puts [ 1, 2, 3 ].map do |e| e + 1 end # do ... end bind with #puts method
#<Enumerator:0x0000010a06d140>
#=> nil
As for the opposite situation, do
... end
in the role of block delimiters cannot always be replaced by curly braces.
[ 1, 2, 3 ].each_with_object [] do |e, obj| obj << e.to_s end # is possible
[ 1, 2, 3 ].each_with_object [] { |e, obj| obj << e.to_s end # invalid syntax
In this case, you would have to parenthesize the ordered argument:
[ 1, 2, 3 ].each_with_object( [] ) { |e, obj| obj << e.to_s } # valid with ( )
The consequence of these syntactic rules is that you can write this:
[ 1, 2, 3 ].each_with_object [ nil ].map { 42 } do |e, o| o << e end
#=> [ 42, 1, 2, 3 ]
And the above also demonstrates the case where {}
are not replaceable by do
/end
:
[ 1, 2, 3 ].each_with_object [ nil ].map do 42 end do |e, o| o << e end
#=> SyntaxError
Lambda syntax with ->
differs slightly in that it equally accepts both:
foo = -> x do x + 42 end # valid
foo = -> x { x + 42 } # also valid
Furthermore, do
and end
themselves do not always delimit a block. In particular, this
for x in [ 1, 2, 3 ] do
puts x
end
and this
x = 3
while x > 0 do
x -= 1
end
and this
x = 3
until x == 0 do
x -= 1
end
superficially contains do
... end
keywords, but there is no real block between them. The code inside them does not introduce a new scope, it is just syntax used to repeat a few statements. Curly braces cannot be used here, and the syntax could be written without do
, such as:
for x in [ 1, 2, 3 ]
puts x
end
# same for while and until
For the cases where {
...}
and do
...end
delimiters are interchangeable, the choice which ones to use is a matter of programming style, and is extensively discussed in another question (from whose accepted answer I took one of my examples here). My personal preference in such case is to emphasize readability over rigid rules.
Basic ruby question: the { } vs do/end construct for blocks
In the "not working" case, the block is actually attached to the puts
call, not to the collect
call. {}
binds more tightly than do
.
The explicit brackets below demonstrate the difference in the way Ruby interprets the above statements:
puts(test = [1, 1, 1].collect) do |te|
te + 10
end
puts test = ([1, 1, 1].collect {|te|
te + 10
})
Block not called in Ruby
Add parentheses to puts
puts(m do
x = 2
y = 3
x * y
end)
The output is 6.
Your code is equivalent to
puts(m) do
x = 2
y = 3
x * y
end
Related Topics
Confusion With the Assignment Operation Inside a Falsy 'If' Block
Rails :Dependent =≫ :Destroy VS :Dependent =≫ :Delete_All
No Such File to Load - Rubygems (Loaderror)
Your Ruby Version Is 2.0.0, But Your Gemfile Specified 2.1.0
Best Way to Create Custom Config Options For My Rails App
What Exactly Is the Singleton Class in Ruby
In Ruby on Rails, How to Format a Date With the "Th" Suffix, as In, "Sun Oct 5Th"
How to Create a Deep Copy of an Object in Ruby
Is There a "Do ... While" Loop in Ruby
How to Install Postgresql'S Pg Gem on Ubuntu
How to Find Which Operating System My Ruby Program Is Running On
Pg::Error: Select Distinct, Order by Expressions Must Appear in Select List
Is There an Efficient Way to Perform Hundreds of Text Substitutions in Ruby