Best explanation of Ruby blocks? [closed]
What is the best explanation for Ruby blocks that you can share?
Both usage and writing code that can take a block?
I offer up my own explanation from this answer, slightly modified:
"Blocks" in Ruby are not the same as the general programming terms "code block" or "block of code".
Pretend for a moment that the following (invalid) Ruby code actually worked:
def add10( n )
puts "#{n} + 10 = #{n+10}"
end
def do_something_with_digits( method )
1.upto(9) do |i|
method(i)
end
end
do_something_with_digits( add10 )
#=> "1 + 10 = 11"
#=> "2 + 10 = 12"
...
#=> "9 + 10 = 19"
While that code is invalid, its intent—passing some code to a method and having that method run the code—is possible in Ruby in a variety of ways. One of those ways is "Blocks".
A Block in Ruby is very, very much like a method: it can take some arguments and run code for those. Whenever you see foo{ |x,y,z| ... }
or foo do |x,y,z| ... end
, those are blocks that take three parameters and run the ...
on them. (You might even see that the upto
method above is being passed a block.)
Because Blocks are a special part of the Ruby syntax, every method is allowed to be passed a block. Whether or not the method uses the block is up to the method. For example:
def say_hi( name )
puts "Hi, #{name}!"
end
say_hi("Mom") do
puts "YOU SUCK!"
end
#=> Hi, Mom!
The method above is passed a block that is ready to issue an insult, but since the method never calls the block, only the nice message is printed. Here's how we call the block from a method:
def say_hi( name )
puts "Hi, #{name}!"
if block_given?
yield( name )
end
end
say_hi("Mridang") do |str|
puts "Your name has #{str.length} letters."
end
#=> Hi, Mridang!
#=> Your name has 7 letters.
We use block_given?
to see whether or not a block was passed along or not. In this case we passed an argument back to the block; it's up to your method to decide what to pass to the block. For example:
def say_hi( name )
puts "Hi, #{name}!"
yield( name, name.reverse ) if block_given?
end
say_hi("Mridang"){ |str1, str2| puts "Is your name #{str1} or #{str2}?" }
#=> Hi, Mridang!
#=> Is your name Mridang or gnadirM?
It's just a convention (and a good one, and one you want to support) for some classes to pass the instance just created to the block.
This is not an exhaustive answer, as it does not cover capturing blocks as arguments, how they handle arity, un-splatting in block parameters, etc. but intends to serve as a Blocks-Are-Lambdas intro.
Ruby blocks are a way of creating Proc
objects which represent code that can be used by other code. Proc objects are instructions between curly braces {}
(or do...end
phrases for multiline blocks, which have lower precedence than curly braces) which may optionally take arguments and return values (e.g. {|x,y| x+y}
). Procs are first-class objects and can be constructed explicitly or attained implicitly as method pseudo-arguments:
-
Construction as a Proc object (or using the
lambda
keyword):add1 = Proc.new {|x| x+1} # Returns its argument plus one. add1.call(1) # => 2
-
Passed as a method pseudo argument, either explicitly using the special
&
last-argument syntax sugar operator or implicitly using ablock_given?
/yield
pair:def twice_do(&proc) # "proc" is the block given to a call of this method. 2.times { proc.call() } if proc end twice_do { puts "OK" } # Prints "OK" twice on separate lines. def thrice_do() # if a block is given it can be called with "yield". 3.times { yield } if block_given? end thrice_do { puts "OK" } # Prints "OK" thrice on separate lines.
The second form is typically used for Visitor patterns; data can be passed to the special block arguments as arguments to the call
or yield
methods.
From Why's (poignant) guide to ruby:
Any code surrounded by curly braces is a block.
2.times { print "Yes, I've used chunky bacon in my examples, but never again!" }
is an example.With blocks, you can group a set of instructions together so that they can be passed around your program. The curly braces give the appearance of crab pincers that have snatched the code and are holding it together. When you see these two pincers, remember that the code inside has been pressed into a single unit. It’s like one of those little Hello Kitty boxes they sell at the mall that’s stuffed with tiny pencils and microscopic paper, all crammed into a glittery transparent case that can be concealed in your palm for covert stationary operations. Except that blocks don’t require so much squinting. The curly braces can also be traded for the words do and end, which is nice if your block is longer than one line.
loop do
print "Much better."
print "Ah. More space!"
print "My back was killin' me in those crab pincers."
end
Block arguments are a set of variables surrounded by pipe characters and separated by commas.
|x|, |x,y|, and |up, down, all_around| are examples.
Block arguments are used at the beginning of a block.
{ |x,y| x + y }
In the above example, |x,y| are the arguments. After the arguments, we have a bit of code. The expression x + y adds the two arguments together. I like to think of the pipe characters as representing a tunnel. They give the appearance of a chute that the variables are sliding down. (An x goes down spread eagle, while the y neatly crosses her legs.) This chute acts as a passageway between blocks and the world around them. Variables are passed through this chute (or tunnel) into the block.
For anybody coming to this question from a C# background (or other langs really), this might help:
Ruby blocks are like lambda expressions and anonymous methods in C#. They are what C# calls delegates (and Ruby calls Procs), which is to say that they are essentially functions that can be passed as values. In both Ruby and C#, they can also behave as closures.
Ruby: { |x| x + 1 }
C#: x => x + 1
Ruby: { |name| puts "Hello there #{name}" }
C#: name => { Console.WriteLine("Hello there {0}", name); }
Both C# and Ruby offer alternative ways to write the above example.
Ruby:
do |name|
puts "Hello there #{name}"
end
C#:
delegate(string name)
{
Console.WriteLine("Hello there {0}", name);
}
In both Ruby and C#, multiple statements are allowed, In Ruby, the second syntax above is required for this.
These concepts are available in many other languages that have been influenced by the ideas behind functional programming.