What is Proc#==

From the description,
---------------------------------------------------------------- Proc#==
prc == other_proc => true or false

 Return true if prc is the same object as other_proc, or if they
 are both procs with the same body.

I thought that a == b in

def block_to_proc(&prc)
prc
end
def test
block_to_proc do
end
end

a = test
b = test

a == b (since both proc have the same body)

However, it returns false. So, the obvious question is: what does “if
they
are both procs with the same body” mean ?

Sylvain J.:

prc

end
def test
block_to_proc do
end
end

a = test
b = test

My guess would be that a and b were created in different contexts, and
that this is why they are not equal. The problem about that is that thay
aren’t really created in different contexts here. They are in this case,
however:

def test(arg)
block_to_proc { arg }
end

a = test(1)
b = test(2)

Obviously a and b are not equal here, because a.call and b.call are not
equal. Note, also:

x = 3
lambda { } == lambda { } # => true
lambda { x } == lambda { x } # => false (maybe because x may have
changed
in between)

Still the behavior you described seems like a bug to me, because of
lambda { } == lambda { }. But I may not see the existing reason why it
isn’t.

Kalman

Hi Kalman, Sylvain, list,

On 2/13/07, Kalman N. [email protected] wrote:

def block_to_proc(&prc)
My guess would be that a and b were created in different contexts, and

Obviously a and b are not equal here, because a.call and b.call are not
equal. Note, also:

    x = 3
    lambda { } == lambda { }     # => true
    lambda { x } == lambda { x } # => false (maybe because x may have changed
                                      in between)

Actually, since blocks are closures, the x is the same variable in
that line. If the next line was x = 5, then the x in both would be
updated. (Well, unless those blocks had been GC’ed by then… :wink:

Still the behavior you described seems like a bug to me, because of
lambda { } == lambda { }. But I may not see the existing reason why it
isn’t.

I believe the “same body” part means that the body was constructed
from the same block. e.g.:

body = lambda{1+2}
lambda(&body) == lambda(&body) #=> true

Or:

def foo
Proc.new == Proc.new
end
foo{1+2} #=> true

There is one exception: empty procs are the “same” no matter how
they’re constructed:

lambda{} == lambda{} #=> true

As opposed to:

lambda{nil} == lambda{nil} #=> false

I agree that the rdoc is a little misleading. Perhaps an example should
follow.

Regards,
George.

Hi,

On Tuesday 13 February 2007 03:59, George O. wrote:

There is one exception: empty procs are the “same” no matter how
they’re constructed:

lambda{} == lambda{} #=> true

As opposed to:

lambda{nil} == lambda{nil} #=> false

it is worth noting the “address” of the empty proc being 0 (NULL? :slight_smile:

lambda{}
=> #Proc:0x00000000@:46(irb)

but I do not understand why:

[lambda{}.object_id, lambda{}.object_id]
=> [-606913178, -606913188]

and:

lambda{} === lambda{}
=> true

? rdoc says that Proc uses Module’s ===, shouldn’t that check for object
identity? what’s going on?

Incidentally, the reason why lambda {} == lambda {} may be because all
the values I showed in the previous post (e.g. data->body, data->var,
etc) are all zero or NULL (no vars, no body, and so on) which would make
them equal and therefore the procs equal.

Maybe.

Hi,

On Tuesday 13 February 2007 07:29, Edwin F. wrote:

Incidentally, the reason why lambda {} == lambda {} may be because all
the values I showed in the previous post (e.g. data->body, data->var,
etc) are all zero or NULL (no vars, no body, and so on) which would make
them equal and therefore the procs equal.

Thank you for your insight! It’s clear now. /me has to remember to
always take
a look at the source. :).

Here’s the C code for proc_eq (in eval.c). It seems that the procs have
to either be the same object, or failing that have the same type
(T_DATA), class, body, variables, scope, in-block local variables
(dyna_vars), and flags.

static VALUE
proc_eq(self, other)
VALUE self, other;
{
struct BLOCK *data, *data2;

if (self == other) return Qtrue;
if (TYPE(other) != T_DATA) return Qfalse;
if (RDATA(other)->dmark != (RUBY_DATA_FUNC)blk_mark) return Qfalse;
if (CLASS_OF(self) != CLASS_OF(other)) return Qfalse;
Data_Get_Struct(self, struct BLOCK, data);
Data_Get_Struct(other, struct BLOCK, data2);
if (data->body != data2->body) return Qfalse;
if (data->var != data2->var) return Qfalse;
if (data->scope != data2->scope) return Qfalse;
if (data->dyna_vars != data2->dyna_vars) return Qfalse;
if (data->flags != data2->flags) return Qfalse;

return Qtrue;

}

The only way so far that I have been able to create two equal procs that
have different object IDs is to clone or dup them. I am sure there must
be another way using some metaprogramming, but I am not experienced
enough in Ruby to find it easily (or maybe ever:)

irb(main):001:0> a = lambda { 4 }
=> #Proc:0x00002b8dd669e828@:1(irb)
irb(main):002:0> b = a.clone
=> #Proc:0x00002b8dd669e828@:1(irb)
irb(main):003:0> a.object_id
=> 23944093823940
irb(main):004:0> b.object_id
=> 23944093815980
irb(main):005:0> a == b
=> true

On Feb 13, 2007, at 1:24 AM, Edwin F. wrote:

The only way so far that I have been able to create two equal procs
that
have different object IDs is to clone or dup them.

Here are a few other ways that seem to be analogous to clone/dup
but via other mechanisms:

a = lambda { 4 }
b = lambda &a
c = proc &b
d = Proc.new &c

def the_block(&block); block; end

e = the_block &d

p a == b # true
p b == c # true
p c == d # true
p d == e # true

This shows that the ‘equality’ of a lambda proc doesn’t get altered via
Kernel#lambda, Kernel#proc, Proc.new, or block argument passing.

But look at:

proc1 = Proc.new { 4 }
lambda1 = lambda &proc1
p proc1 == lambda1 # false

This shows that Kernel#lambda constructs procs that have different
equality semantics than procs from Proc.new.

Gary W.

On 2/13/07, Marcello B. [email protected] wrote:

but I do not understand why:

[lambda{}.object_id, lambda{}.object_id]
=> [-606913178, -606913188]

and:

lambda{} === lambda{}
=> true

? rdoc says that Proc uses Module’s ===, shouldn’t that check for object
identity? what’s going on?

Hmm, where does it say that? Module#===(obj) checks that
obj.is_a?(self), not obj.equal?(self), which doesn’t really make sense
to me for Proc objects.

Looks like Proc#=== falls back to Object#===, which in turn calls #==,
i.e., Proc#==. Whee!

Regards,
George.

On 2/13/07, Edwin F. [email protected] wrote:

if (data->flags != data2->flags) return Qfalse;

return Qtrue;

}

Hmm, so my earlier comment about empty blocks was wrong. You can
actually have unequal empty blocks if you define them in different
contexts…:

def foo(x)
lambda{}
end

lambda{} == foo(1) #=> false

… or with different flags. The return semantics (Proc.new vs.
lambda) is stored as a flag, hence (as Gary noted):

Proc.new{} == lambda{} #=> false

As you pointed out in your other post, body, vars, and dyna_vars seem
to be NULL for empty blocks.

Regards,
George.