Proc vs lambda vs proc

M

Minkoo Seo

Hi group(and probably ruby-talk list - is it running btw?).

I got a question on Proc.new and lambda { ... }. AFAIK, there's two
differences between Proc.new(=proc) and lambda. The first one is that
Proc.new does not check the number of arguments passed to it while
lambda does. The secondis that lambda returns as we expect, i.e., it
returns value, while Proc.new does not.

Though Proc.new lacks several advantages that lambda has, I guess
there might be some situation where Proc.new is more suitable than
lambda. As an example, some code blocks that have to executed
thousands times might run more faster than lambda because it does not
have to check the # of arguments, thereby decreasing computational
overhead.

What is your opinion?

Sincerely,
Minkoo Seo
 
R

Robert Klemme

I got a question on Proc.new and lambda { ... }. AFAIK, there's two
differences between Proc.new(=proc) and lambda.

I think you have it slightly wrong: "proc" and "lambda" are aliases
while "Proc.new" works different.
The first one is that
Proc.new does not check the number of arguments passed to it while
lambda does.

Correct:

irb(main):001:0> f1 = Proc.new {|a,b|a+b}
=> #<Proc:0x003bb2d0@(irb):1>
irb(main):002:0> f2 = proc {|a,b|a+b}
=> #<Proc:0x003b1690@(irb):2>
irb(main):003:0> f3 = lambda {|a,b|a+b}
=> #<Proc:0x003a4d3c@(irb):3>
irb(main):004:0> f1[1,2]
=> 3
irb(main):005:0> f1[1,2,3]
=> 3
irb(main):006:0> f2[1,2,3]
ArgumentError: wrong number of arguments (3 for 2)
from (irb):2
from (irb):6:in `[]'
from (irb):6
from :0
irb(main):007:0> f3[1,2,3]
ArgumentError: wrong number of arguments (3 for 2)
from (irb):3
from (irb):7:in `[]'
from (irb):7
from :0
irb(main):008:0> f2[1,2]
=> 3
irb(main):009:0> f3[1,2]
=> 3
The secondis that lambda returns as we expect, i.e., it
returns value, while Proc.new does not.

I do not understand what you mean here. All three return what you
expect (apart from an exception in the case of wrong # of arguments.
Though Proc.new lacks several advantages that lambda has,

Which advantages do you refer to?
I guess
there might be some situation where Proc.new is more suitable than
lambda. As an example, some code blocks that have to executed
thousands times might run more faster than lambda because it does not
have to check the # of arguments, thereby decreasing computational
overhead.

I suggest to benchmark a concrete example if you are interested in
timings. My guess is that the parameter checking overhead is negligible.
What is your opinion?

I usually use lambda because it most resembles the term "lambda
expression". But I guess this is just a matter of taste / personal
preference.

Kind regards

robert
 
K

Kalman Noel

Robert Klemme:
I think you have it slightly wrong: "proc" and "lambda" are aliases
while "Proc.new" works different.

This is true for Ruby 1.8, but was changed in Ruby 1.9. The decision was
taken because proc and Proc.new are lexically too similar to have different
meanings, IIRC.

Kalman
 
R

Robert Klemme

Robert Klemme:

This is true for Ruby 1.8, but was changed in Ruby 1.9. The decision was
taken because proc and Proc.new are lexically too similar to have different
meanings, IIRC.

Thanks for the heads up! Me not being an early adopter tested with 1.8
only. At least I don't have to change my habit of using "lambda". :)

Kind regards

robert
 
D

dblack

Hi --

Robert Klemme:

This is true for Ruby 1.8, but was changed in Ruby 1.9. The decision was
taken because proc and Proc.new are lexically too similar to have different
meanings, IIRC.

Yes -- this was something that was raised at RubyConf in 2002 or 2003,
and Matz agreed that having proc != Proc.new, while proc == lambda,
was confusing.


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
D

dblack

Hi --

Thanks for the heads up! Me not being an early adopter tested with 1.8 only.
At least I don't have to change my habit of using "lambda". :)

Originally, I think the decision was to deprecate 'proc' entirely --
which would be fine with me, though as long as it's not different from
Proc.new that's OK too.


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
J

Jim Weirich

Minkoo said:
[...]
The secondis that lambda returns as we expect, i.e., it
returns value, while Proc.new does not.

[...] I guess
there might be some situation where Proc.new is more suitable than
lambda. [...]

lambda and Proc.new do handle the return statement in different ways.
Return in lambda returns from the lambda, treating the lambda as an
anonymous function.

Return in Proc.new returns from the enclosing method, treating the block
as an inline piece of code.

The Proc.new behavior is useful where the block is simply a bit the
algorithm. For example:

def member?(target, list)
list.each do |item|
return true if target == item
end
false
end

The explicit return should return from the member? method, not just from
the loop.

-- Jim Weirich
 
M

Minkoo Seo

I usually use lambda because it most resembles the term "lambda
expression". But I guess this is just a matter of taste / personal
preference.

Kind regards

robert

Okay. The problem is that I am writing a ruby tutorial for people from
other langauges (like C++ or Java), but there are three seemingly same
things: block, proc, and lambda. So I need to write about not only the
differences but also the simple but effective rule for choosing one
out of three.

To make it worse, the functionality provided by proc and lambda is
quite similar except for the differences in parameter checking and
return statement handling. And many people suggest to use lambda
instead of proc if one does not have any particular reason. So what
I'd like to do is to verify whether the preference for lambda is
correct or not.

So, my intention is to find out whether there are people who prefer
Proc to lambda whatever the reason is. Up to now, I see none. Any one?

Sincerely,
Minkoo Seo
 
M

Minkoo Seo

Minkoo said:
[...]
The secondis that lambda returns as we expect, i.e., it
returns value, while Proc.new does not.

lambda and Proc.new do handle the return statement in different ways.
Return in lambda returns from the lambda, treating the lambda as an
anonymous function.

Return in Proc.new returns from the enclosing method, treating the block
as an inline piece of code.

The Proc.new behavior is useful where the block is simply a bit the
algorithm. For example:

I guess the following block (do |item| ... end)
def member?(target, list)
list.each do |item|
return true if target == item
end
false
end
The explicit return should return from the member? method, not just from
the loop.

-- Jim Weirich

is block and not a proc.

AFAIK, block is not converted to proc unless the block is captured by
parameter like &blk. Am I wrong?

Sincerely,
Minkoo Seo
 
P

Patrick Fernie

I found the following:
http://innig.net/software/ruby/closures-in-ruby.rb
to be an interesting read on the different flavors of closures in
ruby; I think it takes into account some of the 1.9 changes, but it
may be slightly outdated...

-P

Minkoo said:
[...]
The secondis that lambda returns as we expect, i.e., it
returns value, while Proc.new does not.

lambda and Proc.new do handle the return statement in different ways.
Return in lambda returns from the lambda, treating the lambda as an
anonymous function.

Return in Proc.new returns from the enclosing method, treating the block
as an inline piece of code.

The Proc.new behavior is useful where the block is simply a bit the
algorithm. For example:

I guess the following block (do |item| ... end)
def member?(target, list)
list.each do |item|
return true if target == item
end
false
end
The explicit return should return from the member? method, not just from
the loop.

-- Jim Weirich

is block and not a proc.

AFAIK, block is not converted to proc unless the block is captured by
parameter like &blk. Am I wrong?

Sincerely,
Minkoo Seo
 
P

Phrogz

I found the following:http://innig.net/software/ruby/closures-in-ruby.rb
to be an interesting read on the different flavors of closures in
ruby; I think it takes into account some of the 1.9 changes, but it
may be slightly outdated...

Although certainly interesting, the author seems to have a slightly
too-broad definition of the term closure. He seems to think that
closures must be first-class functions in addition to providing
lexical binding to the environment they were defined in. That's simply
not so.

Further, he seems to be confusing the syntax of Ruby with features.
For example, in a function:
def foo( *a ); end
You refer to array of values by the variable 'a', not by '*a'; just
so, of course you refer to a block in a function
def foo( &b ); end
simply as 'b'...he seems hung up that you can't use &b directly.
 
G

gwtmp01

This thread caused me to do some experimenting and I came across some
Proc semantics that I hadn't seen discussed before and that might
be useful in a DSL context. Consider:

class A
Foo = define_method:)foo) { |x|
p "self: #{self}"
return 'positive' if x > 0
'negative'
}
end

p A::Foo # #<Proc:[email protected]:6>
p A::Foo.call(0) # "self: A" "negative"
p A::Foo.call(1) # "self: A" "positive"
p A.new.foo(0) # "self: #<A:0x25ecc>" "negative"
p A.new.foo(1) # "self: #<A:0x25e2c>" "positive"

I was unaware that define_method returned a proc. I was even more
surprised that it appears to be of the lambda "flavor". That is to
say that a 'return' within the block is relative to the block and not
to the scope where the block is defined.

Previously I thought that only Kernel#proc and Kernel#lambda had
the 'magic' property of altering the return semantics of a block.
I understand why this is also necessary for define_method to be
useful, I was just not aware that the resulting proc was accessible
as the return value of define_method.

I was going to ask if there was any way to write a method like
'define_method' that forces any provided blocks to be interpreted
as lambda vs. 'regular' procs. Then I did some more experimenting:

a = Proc.new { return 42 }
a.call # LocalJumpError

b = lambda &a
a.call # 42

This was another surprise for me as I figured that when lambda was
used, the first proc would be 'wrapped' in a new proc with
lambda semantics, something like:

c = lambda { a.call }

But that isn't the case. If you try 'c.call' you'll get the
LocalJumpError again.

I hadn't see this type of transformation before. It might be
useful in some DSL situations:

class A
filter {|x| x.to_s } # ok
filter {|x| return x.to_s } # potential LocalJumpError
end

If you wrote filter as:

def A.filter(&b)
lambda(&b).call(42)
end

you can avoid the LocalJumpError.


Gary Wright
 
B

Brian Candler

Very interesting! Thanks for sharing your experiments.

Yes, it's interesting to be reminded of all this again.

Much as I love Ruby, I hate having multiple versions of essentially the same
concept which differ in subtle ways. I would find it more aesthetically
pleasing if a tiny Ruby core could bootstrap the whole language - and it
might make it easier to port (and understand).

Now, I wonder why you couldn't approach this problem from the other
direction: instead of having two or more different types of lambda, which
handle 'return' semantics differently, have a single type of lambda and two
different return statements. For example:

return val # return from enclosing method
result val # return from this block only

Internally, you could implement 'return' semantics using catch and throw:

def foo
... some code
end

# could be implemented as something like this:

define_method:)foo) do
catch:)return) do
... some code
end
end

module Kernel
def return(rc=nil)
throw:)return, rc)
end
end

The other difference concerns arity. I don't see why argument checking can't
be done everywhere, as long as you have the option of explicitly allowing
variable arguments where you want them:

p1 = Proc.new { |x=nil,y=nil| ... } # accept 2 or fewer args
p2 = Proc.new { |x, y, *z| ... } # accept 2 or more args
p3 = Proc.new { |*x| ... } # no more single-arg special case!

That is, use the same argument list handling for a 'def' method definition,
a block, and a Proc/proc/lambda.

Anyway, that's just a couple of random thoughts. No doubt it's only when you
implement such a cut-down language that you'd find out what the pitfalls are
in practice :-(

Regards,

Brian.
 
T

Thomas Hafner

Patrick Fernie said:
I found the following:
http://innig.net/software/ruby/closures-in-ruby.rb to be an
interesting read on the different flavors of closures in ruby;

Very interesting, indeed. BTW, in article
<[email protected]> I did the same, i.e. defining a
data structure containing *all* of the Fibonacci numbers:
s = Stream.delay {
Stream.cons(0, Stream.delay {
Stream.cons(1, Stream.map(lambda{|x,y| x+y}, s, s.cdr))})}
(It uses methods posted in article <[email protected]>.)

Regards
Thomas
 
D

dblack

Hi --

Yes, it's interesting to be reminded of all this again.

Much as I love Ruby, I hate having multiple versions of essentially the same
concept which differ in subtle ways. I would find it more aesthetically
pleasing if a tiny Ruby core could bootstrap the whole language - and it
might make it easier to port (and understand).

Now, I wonder why you couldn't approach this problem from the other
direction: instead of having two or more different types of lambda, which
handle 'return' semantics differently, have a single type of lambda and two
different return statements. For example:

return val # return from enclosing method
result val # return from this block only

Internally, you could implement 'return' semantics using catch and throw:

def foo
... some code
end

# could be implemented as something like this:

define_method:)foo) do
catch:)return) do
... some code
end
end

module Kernel
def return(rc=nil)
throw:)return, rc)
end
end

The other difference concerns arity. I don't see why argument checking can't
be done everywhere, as long as you have the option of explicitly allowing
variable arguments where you want them:

p1 = Proc.new { |x=nil,y=nil| ... } # accept 2 or fewer args
p2 = Proc.new { |x, y, *z| ... } # accept 2 or more args
p3 = Proc.new { |*x| ... } # no more single-arg special case!

That is, use the same argument list handling for a 'def' method definition,
a block, and a Proc/proc/lambda.

I think the reason for there not being default arguments in code
blocks is the ambiguity of the pipe character:

{| x=1 | 2 | 3 } # {(x=1) 2 | 3 } or {(x=1|2) 3 } ?

Dave Thomas suggested something at RubyConf 2005 along the lines of:

def m(a,b,c) # method
end

def (a,b,c) # anonymous function
end

I'm not sure what happened to that; I haven't seen it mentioned much
in discussions of this stuff.


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
G

gwtmp01

Now, I wonder why you couldn't approach this problem from the other
direction: instead of having two or more different types of lambda,
which
handle 'return' semantics differently, have a single type of lambda
and two
different return statements. For example:

return val # return from enclosing method
result val # return from this block only

I think Ruby already has this via 'break'. Also, the default behavior
of returning the last expression evaluated in a block is an implicit
'break' in Proc.new procs.

Sometimes you want a Proc to behave like the body of a method (the
lambda flavor of procs) and sometimes you want it to behave like the
body of
an iterator (the Proc.new flavor of procs).
That is, use the same argument list handling for a 'def' method
definition,
a block, and a Proc/proc/lambda.

That seems to be a goal of the 1.9/2.0 line of Ruby as part of
'cleaning up'
the semantics of block argument scope.

I wish there was some better terminology for the two different types
of procs (rather than 'lambda type' and 'Proc.new type'). I think
Pickaxe
uses 'raw proc' but I can never remember which type of proc is being
referenced by that phrase.


Gary Wright
 
B

Brian Candler

I think the reason for there not being default arguments in code
blocks is the ambiguity of the pipe character:

{| x=1 | 2 | 3 } # {(x=1) 2 | 3 } or {(x=1|2) 3 } ?

Dave Thomas suggested something at RubyConf 2005 along the lines of:

def m(a,b,c) # method
end

def (a,b,c) # anonymous function
end

I'm not sure what happened to that; I haven't seen it mentioned much
in discussions of this stuff.

But make this into block syntax and you still have potential ambiguities:

3.times { (i=nil) puts i }

3.times do (i=nil)
puts i
end

(i=nil) is a valid expression/statement in its own right, so you'd need some
more lexical rules to disambiguate this from an argument list. I'd hate more
whitespace-sensitive rules to be added, e.g.

{(i) ... } # block args
{ (i) ... } # expression :-(

Perhaps you use parentheses, and if a block starts with an open-parenthesis
then it's always an argument list. If you want it otherwise, you have to
explicitly avoid it:

3.times { nil, (a=b, c=d) }

It seems the rest of the ASCII set has been used already; e.g.
angle-brackets would have the same problem.

3.times { < i = j > 4 > puts i }

Incidentally, I do see a reason for not doing full argument-checking in
blocks: there are methods which pass block arguments which it's convenient
for the block to ignore if it doesn't want them.

3.times { puts "hello" }
3.times { |x| puts "hello #{x}" }

You could get around this by:

(a) having different methods which pass or do not pass arguments:

3.times { puts "hello" }
3.times_i { |x| puts "hello #{x}" }

(b) making such arguments explicitly ignorable at the caller side, which I
think I prefer. At worst:

class Integer
def times(&blk)
if blk.arity == 1
... loop, yield with index
else
... loop, yield without index
end
end
end

Regards,

Brian.
 
B

Brian Candler

Sometimes you want a Proc to behave like the body of a method (the
lambda flavor of procs) and sometimes you want it to behave like the
body of
an iterator (the Proc.new flavor of procs).

What are those different behaviours?
1. Handling of 'return'
2. Handling of arity
3. Anything else?

If it's just (1) and (2), then I just wonder if you could get the different
behaviours using different keywords, so that there's only a single
underlying proc-like object.

As another idea, if we really need different behaviours, it could be an
explicit attribute of the object:

a = Proc.new:)iterator) { ... }
b = Proc.new:)method_body) { ... }

def proc(opt = :iterator)
Proc.new(opt)
end

def lambda(opt = :method_body)
Proc.new(opt)
end

# or is it the other way round??

Regards,

Brian.
 
B

Brian Candler

(b) making such arguments explicitly ignorable at the caller side, which I
think I prefer. At worst:

class Integer
def times(&blk)
if blk.arity == 1
... loop, yield with index
else
... loop, yield without index
end
end
end

or have a "yieldopt" statement, which does something like

def yieldopt(*a, &blk)
blk.arity == 0 ? blk.call() : blk.call(*a)
end

so the caller is saying that the callee can either have all of the
arguments, or ignore them all.

I can't think of any useful cases where an iterator calls a block with (say)
three arguments, but the block only accepts two and is happy for the third
to be silently ignored.

If the block only accepts one argument, but yield passes more than one, this
is currently treated as another special case:

irb(main):001:0> h = {1=>"one", 2=>"two", 3=>"three"}
=> {1=>"one", 2=>"two", 3=>"three"}
irb(main):002:0> h.each { |k| puts k.inspect }
[1, "one"]
[2, "two"]
[3, "three"]
=> {1=>"one", 2=>"two", 3=>"three"}
irb(main):003:0>

This could be eliminated if people wrote

h.each { |*k| .. I want an array .. }
h.each { |k,v| .. I want separate items .. }

which I think makes the intention very clear. Maybe this is already
considered for 1.9

Cheers,

Brian.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top