
The Use Case for Blocks - zephyrfalcon
http://github.com/raganwald/homoiconic/blob/master/2010/04/blocks.md
======
Xurinos
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.

~~~
raganwald
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.

~~~
Xurinos
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').

    
    
      return jQuery.any(collection, tester) ?
                 'close' :
                 'other value';
    
    

I am still open to the possible usefulness of blocks.

~~~
raganwald
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.

------
akeefer
Ugh. I've always hated the non-local returns in Ruby blocks. They massively
clutter up my mental model of a block as an anonymous function, where a return
statement should return from the block's function, not the enclosing function.

It seems like it adds in a lot of potential confusion and diverges from any
other closure/lambda implementation in any other language without adding much
value.

~~~
ekiru
It doesn't diverge from any other closure/lambda implementation in every other
language. It's just like Smalltalk blocks. I had never thought about it
before, but it does make Smalltalk-style control structures(normal messages
with blocks as parameters) much more convenient to use. Smalltalkers need non-
local return in blocks to be able to do:

foo ifFalse: [^ bar].

doSomething.

Ruby has if and unless and some other control structures with support for
returns, but why not allow control structures implemented as higher-order
functions to include returns, too?

~~~
akeefer
It's a personal taste thing, honestly. Language features aren't free to users:
as a user, you need to understand the behavior, so if there are two possible
behaviors that's something else to learn.

There are two different use cases here: lambdas (which may be true closures)
and user-defined control constructs. In the first case, I'd argue that you
want the declaration semantics to be as close as possible to a normal function
definition. You should be defining a function, so return returns from that
function, and break/continue statements can't have targets outside the
function. For control constructs, you want the code to appear as if its
inline, so you want non-local return semantics. I.e. you want it to behave as
if it's not actually in a separate function at all.

So I think those are two different constructs, and things get confusing when
you conflate the two. At the very least if you want to have those two
constructs, make them look different enough that users don't confuse them.
Java was going to go the wrong way there on their initial closure proposal
(the => versus ==> thing) and make things that look like lambdas behave like
in-line statements. Ruby kind of goes the other way and makes everything look
like a bunch of statements within the body of something else in contexts (like
the reduce() method) where what you really want or would expect is a lambda.
If I write "return x + y" in the body of my argument to reduce(), I expect
that to be the value of the reducer function, not to exit the function that
was calling reduce(). But at least in Ruby a block is always a block and
always has the same semantics, even if those semantics seem inappropriate for
many use cases.

But again, it's personal taste as to which of those, or both, you want to add
to a language. In my personal opinion user-defined control statements aren't
all that useful and you can fill most of the needs with language-defined
control constructs. But it's a tradeoff the language designer has to make, and
there's no one right path.

~~~
ekiru
If you do need lambdas, Ruby does also have those. "lambda { return x + y}"
does what you want it to.

------
arohner
Ugh. I have to say, that's one of my least favorite features of blocks.

In Clojure (which has no blocks or return statement), I would write that code
as

    
    
        (defn outer []
            (first (filter close? something_or_other))
    

What advantage does the block+return code have over that?

~~~
jerf
I have this problem with Ruby and even moreso with Perl. It's not that I don't
understand the reasoning behind $GLORIOUS_FEATURE, my question is how come I
don't miss it when I have to go program in other languages? And how come even
though the other language doesn't have $GLORIOUS_FEATURE it sometimes still
works better in the other language?

Perl <-> Python is my canonical example; I program professionally in Perl and
only as a hobby in Python, so the time spent with each language is probably
about 5:1 (but still quite substantial with both, as I've been programming
with Perl for a long time), so I'm actually more familiar with Perl if
anything. Perl is shot through with features, syntactic nuances, and all sorts
of magical features both by default and in CPAN, and I even use some of them
(selectively), but if it's all so awesome how come I never miss any of it in
Python? (And how come the feature I really miss is the ability to use objects
as the key for hashes, which Python has and Perl does not? Yes, I know about
the CPAN modules, which are hacks and not something I trust, not like Python's
built-in support.)

When I look at languages, I want to look at and talk about the global effects
the language has on my programming. It is far too easy to get caught up in how
awesome this little feature is and how awesome that little feature is and miss
what global effect it is having on your code.

~~~
james2vegas
Because an object does not match the definition of a hash key in Perl, viz.
'Hashes are unordered collections of scalar values indexed by their associated
_string key_ '. You can also use a number or filehandle as a hash key, but
don't expect to use the keys as anything other than strings without some
manipulation.

Just like with ordered hashes, using hashes with non-string keys needs
programming on your part (perhaps overloading stringification for your object
and providing a way to get your object back from the string) or others (CPAN).
Or you could overload (or just add to) the object and provide an accessor for
your value so you can set it on the object itself.

------
rntz
The use case mentioned can be fulfilled by another feature ruby already has,
if I'm not mistaken: first-class continuations. Simply have "return" be
syntactic sugar for invocation of a contination that is implicitly created by
a "def"-defined function. No need for a separate type of object, just add some
syntactic sugar and hey presto.

~~~
abecedarius
Also, you don't need full continuations for this, just continuations with
dynamic extent. (E works this way, plus it defines 'return' as syntactic sugar
for calling the enclosing method's continuation.)

Since I don't know much Ruby I'm not sure if its blocks have other advantages.

------
avibryant
raganwald, you answered "can we do away with blocks". What about "can we do
away with lambdas"? Going by return semantics, Smalltalk has only blocks, not
lambdas. I find that constraint bites me far less frequently than Javascript's
opposite one.

Of course, there's also "can we do away with explicit returns". This is what
Clamato does to be able to compile a Smalltalk-and-Ruby-like language
efficiently into Javascript. You can always abuse exceptions to return early
from however far up the stack when you really need to.

------
JulianMorrison
If you want a nonlocal return, Ruby already has a construct: throw and catch.

