Blocs, Procs and Lambdas
DBC Week 6: Challenge #6
June 6, 2015
If you are as new to programming as I am, you may have experienced what I like torefer to as the sweat reaction that comes from encountering terms and concepts that seem foreign or a bit high-brow. C'mon admit it, you've been there. When seeing a phrase like 'create an algorithm' my heart rate and blood pressure increase, my palms get a little slick and I think to myself 'how the hell do I do that? I studied German not Math!' However, after taking a minute to think, it becomes clear that algorithm is just a fifty-cent word for a list of procedures. I have been creating and using them with relative ease for the past few weeks. Once settled down I can get to work and try to actually learn something.
Try this one on for size. Blocks! Procs! Lambdas! Blocks and Procs and Lambdas, oh my! Is your heart beating a little faster? Are you warmer than you were before reading that last line? Have you run for a paper towel to blot your palms? Well I hope not, I wasn't trying to frighten you.
I'm like to take a minute of your time to break these three terms down into their basic parts: what they are and how they work. Hopefully by the time you finish reading this these terms will become more like a friendly face you are happy to recognize rather than a dude lurking in the shadows with his hood up on a hot night, just staring at you.
Here it goes, short and sweet with examples to follow.
A block is simply a piece of code that can be executed. The block is the code contained between the '{ }', or bound by the 'do' and 'end'. You might recognize it from such syntax as:
vegetables = ["asparagus", "green beans", "broccoli"]
vegetables.each do |item|
puts "I love to eat #{item}!"
end
#=> I love to eat asparagus!
#=> I love to eat green beans!
#=> I love to eat broccoli!
vegetables.each { |item| puts "I love to eat #{item}!"}
#=> I love to eat asparagus!
#=> I love to eat green beans!
#=> I love to eat broccoli!
Thinking about the block in the above example, you realize that you like to eat many other foods than just what is in the vegetable array. If the block from above (I love to eat #{item}!) could be saved as a variable, you then could call it as an argument to a method and use it on other arrays, say fruit, meats or nuts.Now, young Rubyist, meet the proc. In the simplest of terms a proc is a saved block. Proc is its own class, so when you create a new proc, you are creating an object. Check it out: To define a proc set a variable equal to Proc.new followed by the block to be saved:
food_message = Proc.new { |item| "I love to eat #{item}!" }
vegetables.each(&food_message)
#=> I love to eat asparagus!
#=> I love to eat green beans!
#=> I love to eat broccoli!
fruit = ["mangoes", "peaches", "pluots"]
fruit.each(&food_message)
#=> I love to eat mangoes!
#=> I love to eat peaches!
#=> I love to eat pluots!
food_message.call("carrots")
#=>I love to eat carrots!
And now what about lambdas then you say? Well, strangely enough a lamdba similar to a proc in many ways. In fact, ouside of syntax and a couple behaviors, they are really identical. You can even think of a lambda as the shorthand version of a proc (even though it's not all that much shorter when you put them side by side). Let's look at this one step at a time: Lambdas, like procs, are objects. They are defined with the keyword lambda followed by a block, like this:
lambda { |parameter| block }
grouchy_message = lambda { |item| puts "I hate #{item}!" }
vegetables.each(&grouchy_message)
#=>I hate asparagus!
#=>I hate green beans!
#=>I hate broccoli!
grouchy_message.call("okra")
#=> I hate okra!
grouchy_message.call
#=> blog_page:172:in `block in (main)': wrong number of arguments (0 for 1) (ArgumentError)
grouchy_message.call("okra")
#=> I hate okra!
food_message.call
#=> I love to eat !
food_message.call("brussels sprouts")
#=> I love to eat brussels sprouts!
The other fork in the road of how these two work has to deal with how they treat a return. Remember that when you use 'return' in a method definition, once Ruby reads that return, its going to output whatever it is told to return and then leave the method, as opposed to a 'puts' that will output something and then continue with the method definition. If you tell a proc to return something, it is going to evaluate it immediately and leave the method definition. While using 'return' the proc is going to assume all control and exit the method definition. The lambda on the other hand, is going to lay down and let the rest of the method definition run. It will let Ruby go ahead and do the its normal thing of evaluating the last line that it reads. Check out an example to make sense of it:
def proc_test
parent_says = Proc.new { return "Eat your veggies so you grow big and strong."}
parent_says.call
return "I didn't ask how the veggies taste, I told you to eat them!"
end
def lambda_test
parent_says = lambda { return "Eat your veggies so you grow big and strong."}
parent_says.call
return "I didn't ask how the veggies taste, I told you to eat them!"
end
puts proc_test
#=>Eat your veggies so you grow big and strong.
puts lambda_test
#=>I didn't ask how the veggies taste, I told you to eat them!
I hope that this explanation of blocks, procs and lambdas had shed some light on what they are and how they work. Keep in mind that this is just simple introduction into these topics. This was meant to get you familiar with the basis of these Ruby concepts so that you can go forth and expand your understanding. If you find something really cool, send me an email with a link I'd love to check it out. And as always, if you have questions or notice something where I have missed some crucial understanding in this post, please don't hesitate to contact me.