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?
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
}