Block equality

Is there a way to compare two blocks with each other?

If you promote a block into a Proc, how would you know that the
original block was the same?

This is what I want to do

(1..10).each do
  once { puts "Hi Tom"}
end

I've tried to implement once like this

class << self
  def once(&block)
    @ids ||= {}
    block.call unless @ids.key? block.object_id
    @ids[block.object_id] = :true
    puts @ids.keys.join(', ')
  end
end

I've even tried a varient using == and ===

def l(&b)
  @t ||= b
  puts b == @t
  puts b === @t
  @t = b
end

(1..2).each { l{x=1} }

output:
true
false

How can you compare two procs that have the same body?

Thanks,
-- Tom.

···

--
"Nothing will ever be attempted, if all
possible objections must first be
overcome." - Samuel Johnson

"Luck is what happens when
preparation meets opportunity." - Seneca

ruby version:

ruby 1.8.4 (2006-04-14) [i386-mswin32]

···

On 8/15/06, Tom Jordan <tdjordan@gmail.com> wrote:

Is there a way to compare two blocks with each other?

If you promote a block into a Proc, how would you know that the
original block was the same?

This is what I want to do

(1..10).each do
  once { puts "Hi Tom"}
end

I've tried to implement once like this

class << self
        def once(&block)
                @ids ||= {}
                block.call unless @ids.key? block.object_id
                @ids[block.object_id] = :true
                puts @ids.keys.join(', ')
        end
end

I've even tried a varient using == and ===

def l(&b)
  @t ||= b
  puts b == @t
  puts b === @t
  @t = b
end

(1..2).each { l{x=1} }

output:
true
false

How can you compare two procs that have the same body?

Thanks,
-- Tom.

--
"Nothing will ever be attempted, if all
possible objections must first be
overcome." - Samuel Johnson

"Luck is what happens when
preparation meets opportunity." - Seneca

--
"Nothing will ever be attempted, if all
possible objections must first be
overcome." - Samuel Johnson

"Luck is what happens when
preparation meets opportunity." - Seneca

"Tom Jordan" <tdjordan@gmail.com> writes:

Is there a way to compare two blocks with each other?

If you promote a block into a Proc, how would you know that the
original block was the same?

I haven't dived into the code, but I'm pretty sure that (a) promoting
a block to a Proc via &block in the argument list results in a new
Proc each time and (b) ruby 1.8 provides no introspection into the
actual contents of a block.

This is what I want to do

This struck me as interesting, so here's one possible solution:

    class Object
      module OnceHelper; end
    
      def once
        execute = false
        vname = '_' << (begin; raise; rescue; $!.backtrace[1]; end).
          split(':')[0..1].join('_').gsub(/[^A-Za-z0-9]/, '_')
        
        begin
          critical, Thread.critical = Thread.critical, true
          execute = !OnceHelper.instance_variables.include?("@#{vname}")
          OnceHelper.instance_variable_set("@#{vname}", true)
        ensure
          Thread.critical = critical
        end
    
        yield if execute
      end
    end
    
    class OnceDoer
      def try_once
        once { "just this once!" }
      end
    end
    
    doer1 = OnceDoer.new
    p doer1.try_once
    p doer1.try_once
    
    doer2 = OnceDoer.new
    p doer2.try_once
    p doer2.try_once
    
    # >> "just this once!"
    # >> nil
    # >> nil
    # >> nil

N.B. that with this solution a 'once' block is truly executed only
once ever, even across separate instances. If you instead squirrel
the generated instance variables away in the individual object
instance the block will only execute once per instance.

Hope this helps.

-Marshall

Marshall T. Vandegrift wrote:

"Tom Jordan" <tdjordan@gmail.com> writes:

Is there a way to compare two blocks with each other?

If you promote a block into a Proc, how would you know that the
original block was the same?
    
I haven't dived into the code, but I'm pretty sure that (a) promoting
a block to a Proc via &block in the argument list results in a new
Proc each time and (b) ruby 1.8 provides no introspection into the
actual contents of a block.

<snip>

Yes, it's kind of like asking if two equations or two methods are equal.
On the other hand, you could probably use the SerializeableProc (developed here: http://rubyquiz.com/quiz38.html\), add an equality method, and then you'd have it. :slight_smile:

-Justin

Thanks Marshall and Justin.

This class Proc - RDoc Documentation

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

led me to believe that Procs would evaluate to equality if they had
the same body.

So I would have expected that the following:

s = lambda { x = 1 }
t = lambda { x = 1 }
s == t # => true expected, false in reality

So my question is; when would the body of Procs ever be the same?

Thanks,
-- Tom.

···

On 8/15/06, Justin Collins <collinsj@seattleu.edu> wrote:

Marshall T. Vandegrift wrote:
> "Tom Jordan" <tdjordan@gmail.com> writes:
>
>> Is there a way to compare two blocks with each other?
>>
>> If you promote a block into a Proc, how would you know that the
>> original block was the same?
>>
>
> I haven't dived into the code, but I'm pretty sure that (a) promoting
> a block to a Proc via &block in the argument list results in a new
> Proc each time and (b) ruby 1.8 provides no introspection into the
> actual contents of a block.
>
<snip>

Yes, it's kind of like asking if two equations or two methods are equal.
On the other hand, you could probably use the SerializeableProc
(developed here: http://rubyquiz.com/quiz38.html\), add an equality
method, and then you'd have it. :slight_smile:

--
"Nothing will ever be attempted, if all
possible objections must first be
overcome." - Samuel Johnson

"Luck is what happens when
preparation meets opportunity." - Seneca

Tom Jordan wrote:

Marshall T. Vandegrift wrote:
> "Tom Jordan" <tdjordan@gmail.com> writes:
>
>> Is there a way to compare two blocks with each other?
>>
>> If you promote a block into a Proc, how would you know that the
>> original block was the same?
>>
>
> I haven't dived into the code, but I'm pretty sure that (a) promoting
> a block to a Proc via &block in the argument list results in a new
> Proc each time and (b) ruby 1.8 provides no introspection into the
> actual contents of a block.
>
<snip>

Yes, it's kind of like asking if two equations or two methods are equal.
On the other hand, you could probably use the SerializeableProc
(developed here: http://rubyquiz.com/quiz38.html\), add an equality
method, and then you'd have it. :slight_smile:

Thanks Marshall and Justin.

This class Proc - RDoc Documentation

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

led me to believe that Procs would evaluate to equality if they had
the same body.

So I would have expected that the following:

s = lambda { x = 1 }
t = lambda { x = 1 }
s == t # => true expected, false in reality

So my question is; when would the body of Procs ever be the same?

Thanks,
-- Tom.

Hi Tom,

Well, I did find one instance where this works. If you use Proc#dup or Proc#clone, you will end up with a new Proc object, but they will evaluate to being equal:

irb(main):001:0> a = Proc.new { n = 1 }
=> #<Proc:0xb7d36224@(irb):1>
irb(main):002:0> b = a.dup
=> #<Proc:0xb7d36224@(irb):1>
irb(main):003:0> a.object_id
=> -605441814
irb(main):004:0> b.object_id
=> -605445814
irb(main):005:0> a == b
=> true
irb(main):006:0> b == a
=> true
irb(main):007:0> c = Proc.new { n = 1 }
=> #<Proc:0xb7d27ecc@(irb):7>
irb(main):008:0> d = c.clone
=> #<Proc:0xb7d27ecc@(irb):7>
irb(main):009:0> c.object_id
=> -605470914
irb(main):010:0> d.object_id
=> -605475424
irb(main):011:0> c == d
=> true
irb(main):012:0> d == c
=> true

Other than that, I don't see a way for two Procs to have the same block (and know it). Take a look at the source for Proc#==, it's somewhat helpful as well.

I realize this doesn't help you in the general case, though...

-Justin

···

On 8/15/06, Justin Collins <collinsj@seattleu.edu> wrote:

Hi,

This class Proc - RDoc Documentation

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

led me to believe that Procs would evaluate to equality if they had
the same body.

So I would have expected that the following:

s = lambda { x = 1 }
t = lambda { x = 1 }
s == t # => true expected, false in reality

If you really want to check if procs have the same or equivalent bodies, you can use RubyNode (http://rubynode.rubyforge.org/\):

p1 = proc { 1 + 1 }

=> #<Proc:0xb7a70b64@(irb):1>

p2 = proc { 1 + 1 }

=> #<Proc:0xb7a6e580@(irb):2>

require "rubynode"

=> true

p1.body_node.transform

=> [:call, {:args=>[:array, [[:lit, {:lit=>1}]]], :mid=>:+, :recv=>[:lit, {:lit=>1}]}]

p1.body_node.transform == p2.body_node.transform

=> true

p1 == p2

=> false

But please be aware that the equality of the body node does not imply that the blocks have the same closure:

def foo(a) proc { a } end

=> nil

p1 = foo(1)

=> #<Proc:0xb7a6217c@(irb):8>

p2 = foo(2)

=> #<Proc:0xb7a6217c@(irb):8>

p1.body_node.transform == p2.body_node.transform

=> true

p1

=> 1

p2

=> 2

And it is also possible that different Ruby code parses to the same node tree:

p1 = proc { if 1 then 2 else 3 end }

=> #<Proc:0xb7ac83b4@(irb):17>

p2 = proc { unless 1 then 3 else 2 end }

=> #<Proc:0xb7ac239c@(irb):18>

p3 = proc { 1 ? 2 : 3 }

=> #<Proc:0xb7abec4c@(irb):19>

p1.body_node.transform == p2.body_node.transform

=> true

p1.body_node.transform == p3.body_node.transform

=> true

p2.body_node.transform == p3.body_node.transform

=> true

p1.body_node.transform

=> [:if, {:else=>[:lit, {:lit=>3}], :cond=>[:lit, {:lit=>1}], :body=>[:lit, {:lit=>2}]}]

So this is probably not very useful for your once method, but interesting anyway.

Dominik

···

On Wed, 16 Aug 2006 17:21:39 +0200, Tom Jordan <tdjordan@gmail.com> wrote: