views:

149

answers:

2

What does block in ruby mean? It looks similar with smalltalk, but you can't send messages to it.

For example, in smalltalk:

[:x | x + 3] value: 3

returns 6. But in ruby:

{|x| x + 3}.call 3

will cause SyntaxError.

Well, you can pass messages to lambda in ruby, though:

irb(main):025:0> ->(x){x+3}.call 3
=> 6

So in ruby, block is not a block, but lambda is a block? Is this true? I mean, are there any differences between ruby lambda and smalltalk block? If this is true, then what is a ruby block?

Update:

From the comment and answer below, together with some googling, I guess I have more understanding of Ruby block. In Ruby, usually a piece of code evaluates an value, and every value is an object. But, block doesn't evaluate an value. So it's not an object. Instead it can act as part of an object. For example, in {|x| x + 3} can act as a part of the object proc {|x| x + 3 }.

But it did confused me a lot. In smalltalk, almost every expression can be divided into objects (binding to variables are exceptions). It seems in Ruby, there are more exceptions.

+10  A: 

First and the most important thing that Ruby block isn't: an object. It is a syntactic construct, and also obviously has an equivalent implementation - but it is not an object, and thus can't receive messages. Which makes your example of

{|x| x + 3}.call 3

ungrammatical. Lambdas, procs - those are objects that wrap a block, and have a call method which executes the block.

Thus, a block is simply a piece of code which can be passed to a method, outside the argument list - no more, no less. If you pass it to Proc.new constructor, for example, it will wrap it and give you an object you can handle:

Proc.new {|x| x + 3}.call 3
Amadan
I thought mistakenly that Ruby block is some object we pass to the method as its argument. Thanks for clarifying it.
weakish
So if I understand correctly it also mean that when you pass a ruby block as parameter to a method the VM somehow wrap it into a Proc.new?
mathk
I don't know the innards, but I do believe that is incorrect - unless you make it so with the ` yield 3; end; yield3() {|x|puts x};` - Explicit proc (no block passed): `def callprocwith3(func); func.call(3); end; callprocwith3(Proc.new {|x|puts x})` - Converted to proc (wrapping a block): `def callblockwith3( func.call(3); end; callblockwith3() {|x|puts x};` - all do roughly the same thing, but the way you handle it is different. I could have left off the empty parens `()`, but this way it's clearer what is an argument and what isn't.
Amadan
@mathk No, you cannot pass a ruby block as a parameter to a method. And Ruby will report an error instead of converting the block to a Proc instance automatically. It's strange. But in Ruby, block is not an object. And you can not pass it to a method as an argument. I think it's better to avoid the words "send a block", because you cannot "send" it (as in the sense of sending an object). Consider blocks as some kind of keywords or syntax sugar to associate a piece of code with something else. At least this consideration makes my brain more comfortable.
weakish
+1  A: 

A precision:

I would even say that in smalltalk even binding is made up with object. Think of the MethodContext. What you are actually doing is to store the object in the MethodContext. So

a := Object new

Can be rewrite in:

thisContext at: 1 put: Object new.

But obviously you wont write it this way since you need to know were are the temps variable.

mathk