Forum: Ruby What's wrong with lambda and class

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
B2aa9a81f78cbd8aad5b0798fc68f14f?d=identicon&s=25 Ivan Samonov (kronos)
on 2009-03-06 06:30
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?
B2aa9a81f78cbd8aad5b0798fc68f14f?d=identicon&s=25 Ivan Samonov (kronos)
on 2009-03-06 06:31
Sorry the topic is "lambda and call"
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-03-06 12:12
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.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-03-06 12:22
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
E16e84e861c1815ce11ba7bd851c857d?d=identicon&s=25 lasitha (Guest)
on 2009-03-06 12:48
(Received via mailing list)
On Fri, Mar 6, 2009 at 4:50 PM, Brian Candler <b.candler@pobox.com>
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/...

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

FWIW,
lasitha
D7463bd611f227cfb2ef4da4a978a203?d=identicon&s=25 Christopher Dicely (Guest)
on 2009-03-07 03:57
(Received via mailing list)
On Fri, Mar 6, 2009 at 3:45 AM, lasitha <lasitha.ranatunga@gmail.com>
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.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-03-07 09:44
Christopher Dicely 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.
This topic is locked and can not be replied to.