Blocks: They are called closures in other languages, it is a way of grouping code/statements. In ruby single line blocks are written in {} and multi-line blocks are represented using do..end An interesting fact about ruby is that all methods in ruby accept a block, even if you don’t declare a variable to accept it. So for example, take the method below
def my_method puts "Hello World" endIt can accept a block as below
my_method { puts 'Hello Reader' }The code is valid, but the output will have only puts “Hello World”. Why? because we passed in the block but it is not getting called. To run the block passed within your method you need to use the yield command.
def my_method puts 'Hello World' yield endNow it will print
Hello World. Hello Reader.But since we placed yield, it would now be expecting a block to be always passed in. So we need to write code to check if a block is given or not. The command to do that in ruby is block_given?.
def my_method puts 'Hello World' yield if block_given? endProc Is a block itself, but it’s bound to a variable. So proc lets us save a code block to a variable, and pass it around in our application. A good example from the ruby docs is shown below
def gen_times(factor) return Proc.new {|n| n*factor } end times3 = gen_times(3) times5 = gen_times(5) times3.call(12) #=> 36 times5.call(5) #=> 25 times3.call(times5.call(4)) #=> 60Lambda If you felt proc’s to be a refined version of block, then you can say lambda’s to be a refined version of proc. Lambda is essentially a proc itself but, the argument management is rigid. If it doesn’t get an argument or if the argument count is more or less it would raise an error. Example:
Proc.new {|a,b| [a,b] }.call(1,2,3) => [1,2]
lambda {|a,b| [a,b] }.call(1,2,3) => ArgumentError: wrong number of arguments (3 for 2) from (irb):7:in `block in irb_binding' from (irb):7:in `call' from (irb):7 from /home/dev1/.rvm/rubies/ruby-2.2.2/bin/irb:11:in `<main>' 2.2.2 :008 > Proc.new {|a,b| [a,b] }.call(1,2,3)Since it’s now clearer to you on what block, proc and lambda is, let’s start with the difference between the three. Lambda shortcut notation.
code = -> (x) { x*x } code.call 4 => 16Lambda vs proc vs Blocks Proc and Blocks are the same, block is actually creating a single instance of proc which can’t be moved about in a variable. You can read about the similarity of Proc and Block here. If we do return within a lambda, it would just return from lambda and the method will continue.
def lambda_method lambda { return puts 'lambda' }.call return 'method' end lambda_method => methodIf we do return with a proc, it would exit the method and return that value from proc.
def proc_method Proc.new { return puts 'proc' }.call puts 'method' end proc_method => procArguments: The second difference between the three is the way they manage arguments. Block and Proc deal with them more or less the same, but lambda is totally different. Proc and Bloc, doesn’t mind about the number of arguments passed. But if we access a variable that is not present then it would raise an error.
def my_method yield 1, 2, 3 end my_method { |x, y| puts "#{x} & #{y}" } => "1 & 2"
Proc.new { |x, y| puts "#{x} & #{y}" }.call(4, 5, 6) => "4 & 5"lambda will raise an error
def my_method yield end my_method { |x, y| puts "#{x} & #{y}" }Takeaways Block, Proc & Lambda are the three different ways of grouping the code: Block:
- Is in between the curly braces and in between do and end.
- No issue with number of arguments.
- Blocks are basically a proc without a name
- Similar behaviour as Block.
- Can be stored in a variable and move around.
- No issue with number of arguments.
- Return within the proc would exit the method from where it is called.
- Same as Proc, but closer to a method.
- Strict regarding the arguments it gets and it needs.
- Return within a lambda would exit it from the lambda and the method would continue executing.
The described difference in return behavior between procs and blocks is incorrect — there is no difference. In both of them, `return` returns from the method the proc/block was _defined in_. You get ‘unexpected return’ if the context the proc/block was defined in doesn’t allow return, it wasn’t in a method. But in fact they work the same.
I will demonstrate that in the following gist. Also, your examples are confusing because the statement `puts x` actually returns nil for any `x`, so `return puts x` is actually always `return nil`, although with a side effect of printing to console. In my examples I’ll avoid that.
https://gist.github.com/jrochkind/2f01ea0c8472af63ebf3da365b0852ff
Procs and blocks behave identically with regard to `return` — in both cases, the return will cause a return from the _place the proc or block was defined_, and in both cases you’ll get a LocalJumpError if that place doesn’t allow ‘return’ (because it’s not being executed as a method body).
I have corrected my article and also referenced your comment in Reddit, in the article.