Wednesday, November 20, 2013

The many faces of nameless chunks of code in Ruby

The internet is littered with posts about the differences between blocks/procs and lambdas. But when experienced programmers first encounter this topic, they have several questions about it and hence finding themselves having to read several posts or threads. I'm going to try to summarize the answers to several related questions here.

At a very high level as far as the philosophy behind the design of the language goes, there is a question of why there are different ways to do things. One of the guiding principles at play is that there is more than one way to do everything. So if it makes sense to have two different approaches to do something that are convenient in different contexts, then both approaches often exist so that you have the more convenient option at hand.

Another important guiding principle that's a part of ruby is the principle of least astonishment. I'm just mentioning it here but I'll talk about why its relevant further down.

On a mechanical level it's important to understand the differences between lambdas and blocks/procs. Here is one source. The important differences are argument checking, and the behavior of return, break and continue. It's important not only to understand how they are different, but that there are things you can do with lambdas that you cannot do with blocks and there are things you can do with blocks that you cannot do with lambdas.

The next thing to understand is what they are.

  • Lambdas are lambdas just like you see in most other languages where anonymous functions exist. It's a closure that you can pass around as a parameter or return and then call. 
  • Blocks are something completely new. They are a syntactic construct, not first class objects. At a superficial level they behave like lambdas (with the restrictions that they can only be the last parameter and so on) and the differences between blocks and lambdas have been described above.
  • Procs is like an object representation of a Block. You use a proc when you'd like to accept a block and then do something funky with it. The real decision api wise that you must make is whether you want to accept a lambda or accept a block. Procs are what you get when you peek under the hood and decide that you really need to something a little more unusual.
People who are used to lambdas find blocks and procs inelegant and confusing. Inelegant because there already is a mechanism to pass closures around. Confusing because it has unusual semantics. Once you have internalized what they are and what they can do differently, it's easy to see that these differences at times make certain things possible that would not have been possible otherwise. 

But let's talk about this being confusing and the principle of least surprise. The reason that this seems confusing is because it's not how closures in other languages behave. But the principle of least surprise has never been about compatibility with concepts from other languages. All that the POLA says is that once you understand how something works and how to think about it, apis will ideally behave in a non-surprising manner. It does not imply that you will not have to learn how ruby works. What it DOES imply though is that there is a conceptual model that can be used to understand blocks that is different from the conceptual model people use for lambdas. One where the behavior of return in blocks is not confusing at all.

The way to think about blocks is that the code inside a block belongs to the function in which it is defined, and hence the semantics of the code internally should behave exactly as it would if it were outside the block. A block is not a function that you are passing in ... that's a detail of how it's implemented. A block is a chunk of code that is from your function that executes when the method you call chooses to activate it. And since its a chunk of code from your function, the return statement continues to behave the way it would behave if it were not inside the block. Code inside the block should not be thought of as special from code outside. It just happens to run at a time that's not of your choosing. it's a different mental model.
Continue and break are special keywords for code inside a block that break out of the block and break out of the function that took the block respectively. This makes them consistent with loops. Since blocks are often used as iterators, break and continue behave just the same inside iterators and regular loops.
  


5 comments:

  1. Superb. I really enjoyed very much with this article here. Really it is an amazing article I had ever read. I hope it will help a lot for all. Thank you so much for this amazing posts and please keep update like this excellent article.thank you for sharing such a great blog with us. expecting for your.
    Digital Marketing Company in India

    ReplyDelete
  2. Great Article… I love to read your articles because your writing style is too good, its is very very helpful for all of us and I never get bored while reading your article because, they are becomes a more and more interesting from the starting lines until the end.

    Aws Training in Chennai

    ReplyDelete
  3. Superb i really enjoyed very much with this article here. Really its a amazing article i had ever read. I hope it will help a lot for all. Thank you so much for this amazing posts and please keep update like this excellent article.

    Laser Root Canal Treatment In Chennai

    Best Dental Clinic In Velachery

    ReplyDelete
  4. Interesting blog which attracted me more.Spend a worthful time.keep updating more.
    Digital marketing company in Chennai

    ReplyDelete