What's wrong with lambda and class

Code:

def call_block(&c)
c.call
end

def yield_block(&c)
yield
end

def lambda_and_call
c = lambda { return 1 }
call_block(&c) + 1
end

def lambda_and_yield
c = lambda { return 1 }
yield_block(&c) + 1
end

def proc_new_and_call
c = Proc.new { return 1 }
call_block(&c) + 1
end

def proc_new_and_yield
c = Proc.new { return 1 }
yield_block(&c) + 1
end

[:lambda_and_call, :lambda_and_yield, :proc_new_and_call,
:proc_new_and_yield].each {|m| puts “#{m}: #{send(m)}”}

Result:

lambda_and_call: 2
lambda_and_yield: 1
proc_new_and_call: 1
proc_new_and_yield: 1
Why 2?

Sorry the topic is “lambda and call”

All fully explained at
http://innig.net/software/ruby/closures-in-ruby.rb

But in summary, the key difference between Proc/block and lambda is that
return within a Proc/block returns from method it was invoked within,
whereas return within a lambda just returns from the lambda itself.

Actually, the question for me was “why 1?” in your second case. Passing
a lambda as a block argument to a method is an odd thing to do; I
believe it is being coerced to a block as if you did Proc.new(&c).

Maybe this makes it clearer:

def call_block(&c)
c.call
end

def lambda_and_call
c = lambda { return 1 }
call_block(&c) + 1 # the lambda returns 1
end

p lambda_and_call

def yield_block(&c)
yield
end

def lambda_and_yield
c = lambda { return 1 }
x = yield_block(&c) # the entire method returns 1
STDERR.puts “This line is not reached!”
x + 1
end

p lambda_and_yield

On Fri, Mar 6, 2009 at 3:45 AM, lasitha [email protected]
wrote:

03> end
definitive on this yet.
The idea that it has to do with yield is basically right, though I’d
describe it as having to do with blocks as distinct from instances of
class Proc (procs or lambdas).

When you yield to a block attached to a function – which, you must
keep in mind, isn’t a proc or a lambda or even a normal Ruby object at
all, its a different beast entirely though you can call a method with
the method(&proc) notation to send a Proc instance (proc or lambda) to
be wrapped in a block* – it behaves like a proc (that is an object
created with Proc.new {…} in any version of ruby, and also proc
{…} in Ruby 1.9.x.) Both “lambda_and_yield” and “proc_and_yield”,
therefore, aren’t yielding to lambdas or procs, the methods yield to a
block in both cases, so the behavior is the same, and the same as it
would be if you attached a block to the method call directly instead
of supplying a Proc instance to wrap in a block by calling with the
method(&proc) notation.

  • I’ve often seen it described as “treated as” or “converted to” a
    block, but its clear that it retains its identity, since if you define
    a method with def method(&block) to make a passed Proc instance or
    block accessible within the method body as a Proc instance, the local
    Proc instance is still a lambda if you passed in a lambda, which
    suggests to me that a call to the Proc instance gets wrapped in a
    block if the method doesn’t use the &block argument pattern to get it
    as a Proc instance. The Pickaxe (2nd Ed.) does discuss the behavior of
    blocks, procs (which it calls “raw procs”), and lambdas in quite some
    detail, on pp. 356-360.

On Fri, Mar 6, 2009 at 4:50 PM, Brian C. [email protected]
wrote:

Actually, the question for me was “why 1?” in your second case. Passing
a lambda as a block argument to a method is an odd thing to do; I
believe it is being coerced to a block as if you did Proc.new(&c).

That’s what i’m trying to figure out, too.
If the lambda is being coerced, it isn’t being honest about it:

01> def is_lambda?(&closure)
02> closure.lambda?
03> end
04> is_lambda? &(Proc.new { })
→ false
05> is_lambda? &(lambda { })
→ true

Perhaps it has more to do with rules around yield (or it’s
implementation)?

All i’ve dug up so far is this (unanswered) thread:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/22634

Been scanning the Pickaxe and Flannagan/Matz but haven’t seen anything
definitive on this yet.

FWIW,
lasitha

Christopher D. wrote:

When you yield to a block attached to a function – which, you must
keep in mind, isn’t a proc or a lambda or even a normal Ruby object at
all, its a different beast entirely though you can call a method with
the method(&proc) notation to send a Proc instance (proc or lambda) to
be wrapped in a block* – it behaves like a proc (that is an object
created with Proc.new {…} in any version of ruby, and also proc
{…} in Ruby 1.9.x.) Both “lambda_and_yield” and “proc_and_yield”,
therefore, aren’t yielding to lambdas or procs, the methods yield to a
block in both cases

But the object_id is the same, so it’s not wrapped in a “block” object.
And neither, I think, is it mutated. There is “is_lambda?” in 1.9, but
no “is_block?”

which
suggests to me that a call to the Proc instance gets wrapped in a
block if the method doesn’t use the &block argument pattern to get it
as a Proc instance.

But the only way to get hold of the block argument without &block is to
use yield. So it is equivalent to say that yield has special semantics.

The Pickaxe (2nd Ed.) does discuss the behavior of

blocks, procs (which it calls “raw procs”), and lambdas in quite some
detail, on pp. 356-360.

It’s somewhat incomplete. p357-p358

"There are three ways of converting a block into a Proc object:

  1. By passing a block to a method whose last parameter is prefixed with
    an ampersand. That parameter will receive the block as a Proc object"

But it doesn’t say what happens if you pass a lambda, using the &
notation at the caller.

It says that a block is not an object at all, and it goes on to make a
distinction between two types of real objects: a “raw proc” and a
“lambda”, which are the two flavours of Proc object.

I’m not sure whether it makes much sense to consider a block as a third
class of entity, which is not an object at all. If I do

def receiver(&blk)
STDERR.puts blk.object_id
end

foo = lambda { “Whoa” }
STDERR.puts foo.object_id
receiver(&foo)

then blk in receiver is exactly the same object as foo at the caller.