kjo kjo - 4 years ago 67
Ruby Question

What's the rationale for the block argument?

I'm just learning about Ruby, and am having trouble seeing the point of the so-called "block argument" (which can be appended to method invocations).

AFAICT, the "block argument" works like any run-of-the-mill callback argument in other programming languages (or in Ruby itself, for that matter), but are more limited in that one can attach at most one "block argument" to a method invocation, while there's no upper limit on the number of callback arguments a method/function can be designed to accept.

(In case clarification is needed, by "callback" all I mean is a function F that another function G receives (as one of its arguments) and in turn calls.)

I figure that there must be something that one can do with a block argument that can't be done just as easily with a callback (otherwise, it's hard to justify supporting a special syntax for block arguments), but I can't figure out what this "something" could be.

My question is just that:

What can be done with a "block argument" that couldn't be done with regular callback?




EDIT: An earlier comment (now deleted) said that block arguments are better described as "closures" than as "callbacks". In my book, closures are a special case of callbacks. One can, if one wants, replace "callback" with "closure" in my post. The post's question still remains (unless, of course, "block arguments" were the only way to create closures in Ruby, but if this were the case, it in itself would raise more questions than it answers).

Answer Source

I figure that there must be something that one can do with a block argument that can't be done just as easily with a callback (otherwise,

Yes. Blocks are a sort of "syntactic sugar", but they are also functionally quite different from "callback function" in a language like JavaScript.

For one thing, the return keyword functions very differently than it does in functions and methods both in Ruby and other languages.

Inside a block, return jumps out of the containing method, while in a method return jumps out of (obviously) the method.

This is important for the following case:

def find_by_email(users, search_email)
  users.each do |user|
    # return all the way out of find_by_email
    return user if user.email == search_email
  end

  nil
end

Here, we're using each to iterate over a collection. If some condition is me inside the block, the user is returned all the way out of the containing method.

This doesn't work with a function. Compare the "identical" JavaScript code, which doesn't work as intended:

function findUserByEmail(users, searchEmail) {
  users.forEach(function (user) {
    if (user.email == searchEmail) {
      // return out of `forEach`
      return user;
    }
  });
  // broken
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download