Daniel Brockman wrote:
"Adam P. Jenkins" <thorin@theshire.com> writes:
In all other languages I've used that have closures, such as Scheme,
ML, Haskell, OCaml, and even Javascript, a closure is just an object
like any other object, that happens to have a special literal syntax
for creating them.
[...]
The way ruby does it with blocks/yield has several disadvantages:
1) There's an arbitrary limit of 1 on the number of blocks you can
pass to a function. If you want to pass more than 1, you need to
convert all but the last block to a proc.
The languages you mentioned above all have this ``arbitrary limit.''
Consider O'Caml. You can only give one block to the `fun' keyword.
Or JavaScript. Certainly, the `function' keyword only takes one
argument list and one code block.
The "function" keyword is not a function, it's a keyword for defining a function, equivalent to "def" in Ruby, or "fun" in O'Caml. I'm talking about the passing multiple closures to a function. In Ruby you need to use a different syntax to pass more than one closure argument than if you only want to pass one. That's what I was talking about.
obj.method_that_takes_one_block {|a,b| .... }
obj.method_that_takes_two_blocks(proc{|a| ... }) {|b| ... }
What would seem cleaner to me would be if the {|params| code } syntax evaluated to a proc. Then you'd just say:
obj.method_that_takes_two_blocks {|a| ... }, {|b| ...}, third_arg
See, in the last (hypothetical) syntax, proc is just a kind of object that happens to have a literal syntax for defining it, just as arrays and hashes do, but is otherwise just like any other object, and is passed as an argument just as anything else is passed as an argument. That's all I'm suggesting, and wondering why it's not that way, or if it couldn't be made that way in Ruby 1.9.
In fact, these languages have the additional ``arbitrary limit'' that
code blocks cannot be passed to user-defined methods.
What I mean is that Ruby allows this,
lambda { |a, b, c| ... }
as well as this
moomin { |a, b, c| ... }
and this ---
snufkin foo, bar do |a, b, c| ... end
Heck, Ruby 1.9 even allows this shorthand for lambdas,
{ |a, b, c| ... }
but that's beside the point. The point is that JavaScript only
allows this:
function (a, b, c) { ... }
That is to say, you cannot define `moomin' in a way that makes this
valid JavaScript code:
moomin (a, b, c) { ... }
This is just a semantic game having to do with the overloading of the word "block" in different language definitions. In the Ruby definition the word "block" is used to mean a type of anonymous closure, whereas in most other languages the word "block" is usually used to refer to an enclosed section of code, such as what would be executed inside an if or else branch. You can do all the Ruby things you're showing above with Javascript, it's just that the syntax is different, and what Ruby calls a "block", Javascript calls a "closure", or "function object".
The Javascript equivalent of what Ruby calls a "block" would be this:
Ruby:
{|a, b| somecode }
Javascript:
function(a, b) { somecode }
Except that Javascript doesn't have two different types of closures, so the Javascript closure is really closer to a "proc" in Ruby, since it evaluates to a Function object, which can be directly assigned to a variable, or passed as a parameter to a function.
The same goes for all the other languages. (Well, except for Scheme,
which really actually lets you define arbitrary syntactic forms, but
we all know that Lisp is inherently superior to everything else, so I
will just pretend you didn't mention it.)
Clearly, when it comes to blocks, Ruby's syntax is far more general
than that of other mortal (i.e., non-Lisp) languages.
While a JavaScript programmer would go like this,
array.sort(function (a, b) { ... })
a Ruby programmer could go like this ---
array.sort &lambda { |a, b| ... }
that would be the literal translation. But Ruby takes another step;
it lets you give code blocks directly to _any_ method,
array.sort { |a, b| ... }
not just `lambda'. This is _better_ than most languages can do.
Semantically,
array.sort {|a,b| .. }
is the same as Javascript's
array.sort(function(a,b) { ... })
so you're still just playing semantic games with Ruby's unusual use of the word "block" to claim some fundamental advantage for Ruby. Basically the difference just comes down to the fact that you don't have to use the "function" keyword in Ruby. The real difference, and the one that I'm questioning the usefulness of, is the fact that Ruby provides several different ways to do the same thing:
def func1
yield
end
def func2(&block)
block.call
end
def func3(block)
block.call
end
blockvar = lambda { ... }
func1 { ... }
func1 &blockvar
func2 { ... }
func2 &blockvar
func3(lambda { ... })
func3 blockvar
I wouldn't see anything wrong with having all these different ways of passing closures if I could see some different functionality being offered by the different methods. As it is, I'm a little baffled as to why they all couldn't be unified down to
def func(block)
block.call
end
func { ... }
And for a function that takes two blocks:
def func(block1, block2)
block1.call
block2.call
end
func { ... }, { ... }
Etc. Is it really just a matter of backward compatibility at this point? (By "just", I don't mean to suggest this is an insignificant concern.) Is it that people like not having to type in the extra parameter when they're defining a function? Is it that "yield" inherently allows for faster execution in the common 1 block case? Is there some other advantage to "yield" that I'm missing?
Adam