Or you could use jQuery's filter() to find if there is at least one match. Even better is to use an any() function that can shortcut.
The real issue here is that each() is incorrectly used to perform a search. each() is intended to perform an operation on each member of a set, not query information about the members of the set.
This is not a use case for blocks but an example of an incorrect transformation of the imperative for loop.
You confuse jQuery's .each() with its jQuery.each() function. The first is a method on a jQuery DOM collection, and indeed you could use .filter() or .any() on a colleciton of DOM elements.
jQuery.each() operates on an arbitrary array, .filter() cannot be applied to it. I could have written my own, of course, but doing so would have simply meant wrapping a for loop in a function.
This is a crucial point. The example is NOT bad even if there was a jQuery.select() or jQuery.filter() analogue for arbitrary arrays. The point is that a for loop is a syntactic construct with certain properties, and that you cannot make a direct analogue out of functions. Assuming there was a jQuery.any() function, I could write:
if jQuery.any(collection, function (...) {...}) return 'close';
But I am still moving the return statement out of the block of code. Ruby blocks allow me to leave it where it is.
The use case for blocks is when you want to leave the return statement where it is just like in a for loop. The answer of 'use a functional programming construct that works with pure functions' is equivalent to the old Unix answer to any question: 'How do you do X? Don't do X, do Y.'
It is informative and useful to point out that there are elegant ways of not needing a return statement inside a loop. Thanks! But I don't think it invalidates the point that this is where blocks replicate a built-in feature of keywords like 'for' and functions do not.
I think a clearer way to put the distinction is that in Ruby, blocks serve both as lambdas AND to let you define new control structures (that are thus analogous to for loops). Combining the two lets you do things that you can't do just with lambdas, and if you want to make an argument as to why blocks are flexible that's the right argument to make. For example, to wrap file access in the appropriate open/close functionality while still letting you return early via the block.
Unfortunately, it can also result in some serious confusion on the part of a programmer that wants to think of them as lambdas. Java was originally going to do something similar and have two types of lambdas in 1.7, one using => and the other using ==> to indicate the non-local return semantics, and I think it would have confused a ton of people.
If you have a filter that takes a predicate function, for example, you might naively do (excuse my incredibly-rusty-and-thus-totally-syntatically-mangled-Ruby):
some_list.filter() |x| do
if (x.foo)
return true
else
return false
end
In this case, you'll end up returning from the outer function, NOT just returning a true/false value for filtering. To do that, you have to just do:
some_list.filter() |x| do
if (x.foo)
true
else
false
end
And rely on the way that things you might otherwise think of as statements are expression values in Ruby. But what if my predicate function is really complicated, and I want to end the block early? Um . . . too bad. Better put that in its own function and call that function from your block.
I'm sure that in practice those aren't major issues for people used to Ruby idioms and behavior, but from someone new to Ruby they can really trip you up.
You could use grep() instead of filter(). Sorry for not including it in my first post... Granted, it still suffers the problem of lacking the shortcut, but programming new useful abstractions is part of our job anyway. The point is to make it readable.
I am skeptical that this example proved the thesis: "What is there you can't do with lambdas such that you need to add blocks?" unless the point was that you have the "benefit" of performing a goto/return out of a block and not out of a strictly scoped lambda. It seems like a step backward to me. For this example anyway, the better abstraction would be any() -- to treat the set of gestures as a queryable entity and to react to that query.
I haven't played with ruby in years, so forgive my syntax mistakes, but I do not think people would find
i = 0
something_or_other.each do |e|
return i if null(e)
i++
end
to be a better abstraction than
return e.length
Come to think of it, you have another return path downstream in that function anyway (not 'close').
For me it comes down to this: If the language uses blocks for its own constructs, it ought to let you use blocks for yours. If blocks are a bad idea for your constructs, then they ought to be a bad idea for `if` and `for` and all the other constructs languages like Javascript provide that have blocks of their own.
while (true) {
if (testState()) return valueOfState();
}
moreStateChangingStuff()
as opposed to:
var flag = false, result = null;
loop(function() {
if (testState()) {
flag = true
result = valueOfState()
}
})
if (flag) return result
moreStateChangingStuff()
I think there is a case that blocks exist to bridge the bap between the imperative and functional code. If you picked just one paradigm or the other you wouldn't need blocks, but when you try to pick-and-choose which you use where you find that you need blocks over lambdas.
Is there a significant benefit to writing this sort of in-between code, or do you think we should pick one or the other whenever possible?
Sure, this example is bad, but can you really not see the value of an abstraction based on the behavior of the kind of block of code that is attached to an if or for statement?
The real issue here is that each() is incorrectly used to perform a search. each() is intended to perform an operation on each member of a set, not query information about the members of the set.
This is not a use case for blocks but an example of an incorrect transformation of the imperative for loop.