Paul, Again … good comments. I have just a few comments on your
blocks section.
On Blocks
You say that Python has generators which are equivalent to Ruby’s
blocks. I think that blocks and generators are mostly unrelated. […]
Yes and no. Although they are entirely different things, they both are
used for do “iteration-like” stuff in their respective languages. That
and the fact they both use the keyword “yield” (although with complete
different semantics!) leads people to confuse the two.
Ruby Blocks are nothing more than a little syntactical sugar for
anonyous functions/closures. The Ruby yield is nothing more than a
function call to a special anonymous function attached to a method.
A Python generator is really a form of coroutine. The Python yield
statement doesn’t follow function call/return semantics at all, but
allows the generator to switch between the main code and the generator
code in coroutine style.
But generators do allow you to do something that is very difficult
without them. Consider this code:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66316
Actually, this code is easy to duplicate with blocks …
def fib
x = 0
y = 1
proc {
x, y = y, x + y
x
}
end
g = fib
9.times { print g.call, " " }
puts
This problem is was easy to convert because the basic loop is easy to
unroll. If the algorithm were more deeply nested, or used yield in
multiple locations, the conversion would be more obscure. For example,
a generator that returned the leaves of a tree data structure would be
fairly easy using generators, but more difficult without.
For those tougher problems, a Ruby programmer might resort to using
continuations. Continuations are much more powerful than generators,
but also much harder to use and understand.
So, in summary, this is how I see it. Ruby uses blocks/closures for
most of its iteration needs, switching to continuations or other
mechanisms for the hard cases. Python uses a more powerful technique
that handles the common case and some of the harder cases.
One more minor thing …
[…] but you can only pass one block (unless you wrap it in a
function!). I think.
No, you would simply pass a proc object instead …
def upload(handle_data_proc, output_proc, print_error_proc, etc)
…
upload(proc{|data| blocks.append(data)},
proc{|data| logger.write_msg(data)},
proc{ logger.write_err(data)},
5)
···
On Sun, 2003-03-23 at 16:17, Paul Prescod wrote:
“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)