What is Proc#==

S

Sylvain Joyeux

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 ?
 
K

Kalman Noel

Sylvain Joyeux:
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

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
 
G

George Ogata

Hi Kalman, Sylvain, list,

Sylvain Joyeux:

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)

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... ;-)
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.
 
M

Marcello Barnaba

Hi,

There is one exception: empty procs are the "same" no matter how
they're constructed:

=A0 lambda{} =3D=3D lambda{} =A0#=3D> true

As opposed to:

=A0 lambda{nil} =3D=3D lambda{nil} =A0#=3D> false


it is worth noting the "address" of the empty proc being 0 (NULL? :)
=3D> #<Proc:0x00000000@(irb):46>

but I do not understand why:
[lambda{}.object_id, lambda{}.object_id]
=3D> [-606913178, -606913188]

and:=3D> true

? rdoc says that Proc uses Module's =3D=3D=3D, shouldn't that check for o=
bject=20
identity? what's going on?
--=20
pub 1024D/8D2787EF 723C 7CA3 3C19 2ACE 6E20 9CC1 9956 EB3C 8D27 87EF
 
E

Edwin Fine

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@(irb):1>
irb(main):002:0> b = a.clone
=> #<Proc:0x00002b8dd669e828@(irb):1>
irb(main):003:0> a.object_id
=> 23944093823940
irb(main):004:0> b.object_id
=> 23944093815980
irb(main):005:0> a == b
=> true
 
E

Edwin Fine

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.
 
M

Marcello Barnaba

Hi,

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. :).
 
G

Gary Wright

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 Wright
 
G

George Ogata

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;
}

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.
 
G

George Ogata

but I do not understand why:
[lambda{}.object_id, lambda{}.object_id]
=> [-606913178, -606913188]

and:=> 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.
 

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,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top