Forum: Ruby rebinding a Proc

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.
unknown (Guest)
on 2006-02-10 22:43
(Received via mailing list)
Weird stuff ahead.  I'm making (actually, remaking) a DSL.  I'm
re-visioning
RHDL to take more advantage of metaprogramming tricks.

So I'd like the user to be able to declare a logic gate like so:

  class AndGate < RHDL
    inputs  :a, :b
    inouts  :bus
    outputs :a_and_b

    define_behavior {
      puts "Inside of define_behavior: a is: #{a}, b is: #{b}"
      a_and_b = a & b
    }
 end

And then the user should be able to instantiate an AndGate like so:

  andg = AndGate.instance() #optional args


The 'inputs', 'inouts' and 'outputs' methods create accessors for the
signals
at the class level so that they are then accessable in the block passed
to
define_behavior.

Here's some of the background from the RHDL class:

class RHDL
  class << self
    def instance(*args)  #clones this class.
      puts "instance:self is: #{self}"
      klass = self.clone
      puts "klass.class is: #{klass.class}"
      klass
    end
    def define_behavior(&b)
      @__behavior = b
      puts "@behavior.class is: #{@__behavior.class}"
    end
    def behavior
      @__behavior
    end
    def run
      puts "in #{self}::run"
      puts "a is: #{a}, b is: #{b} "
      @__behavior.call
    end
    def inports
      @__inports ||=[]
    end
    def inoutports
      @__inoutports ||=[]
    end
    def outports
      @__outports ||=[]
    end
    def create_accessor str
      instance_eval "def #{str}=(val); puts \"self is: #{self}\";
@#{str} =
val; end"
      instance_eval "def #{str}; @#{str} ||=0; end"
    end
    def inputs *in_syms
      in_syms.each {|input|
        inports << Port.new(input.to_s)
        create_accessor input
      }
    end
    #... outputs, inouts, etc.
end


Of course, the problem is in the block passed to the define_behavior
method.
It's defined within the context of the AndGate class definition above,
so if
we clone the AndGate class itself we find that the behavior is still
determined
by the context of the original AndGate class.

So If I do:

  AndGate.a=1; AndGate.b=1
  AndGate.run #=> 1

  a = AndGate.instance()
  a.a=0; a.b=0
  a.run #=> 1  (but should be 0; this is because the block is still
evaluated
                in the context of AndGate class, not the clone of
AndGate)


So when the AndGate class is cloned, is there a way to unbind the proc
in the
class' @__behavior class inst var and rebind it in the context of the
new
cloned class? (kind of what can be done with methods and UnboundMethod)

[I realize there are probably other ways to do this where this wouldn't
be an
issue (use a method instead of a proc, for example) but I'm trying to
maintain
compatibility with the backend simulation guts of the current RHDL
system.]

Phil
unknown (Guest)
on 2006-02-10 23:14
(Received via mailing list)
Sorry to respond to my own message, but sometimes it helps to write
these
things out.  Here's how I did it (look for changes in the RHDL class
below):

>      a_and_b = a & b
>  class << self
>    def instance(*args)  #clones this class.
>      puts "instance:self is: #{self}"
>      klass = self.clone
>      puts "klass.class is: #{klass.class}"
       @ubm.bind(klass)  #bind the unboundmethod to the cloned class
       meth = klass.method(:do_it) #get a method object from the newly
                                   # bound 'do_it' method.

       klass.set_behavior &meth    #set cloned class' @__behavior proc
                                   #to the new method object
                                   #('&' converts to proc)
>      klass
>    end
>    def define_behavior(&b)
>      @__behavior = b
       self.class.send(:define_method, :do_it, &b) #define a 'do_it'
method
       @ubm = self.class.instance_method(:do_it) #create an unbound
method
>      puts "@behavior.class is: #{@__behavior.class}"
>    end
     #add a set_behavior method:
     def set_behavior &b
       @__behavior = b
     end


>    end
>    end
>    def inputs *in_syms
>      in_syms.each {|input|
>        inports << Port.new(input.to_s)
>        create_accessor input
>      }
>    end
>    #... outputs, inouts, etc.
>end
>
>

Now it seems to work:
>
>  AndGate.a=1; AndGate.b=1
>  AndGate.run #=> 1
>
>  a = AndGate.instance()
>  a.a=0; a.b=0
>  a.run #=> 0    #a now is independent of AndGate
>


....seems to work, but it also seems a bit convoluted ;-)

Phil
Erik V. (Guest)
on 2006-02-11 00:15
(Received via mailing list)
You might want to replace this:

 @__behavior.call

.... with this:

 instance_eval(&@__behavior)

gegroet,
Erik V. - http://www.erikveen.dds.nl/
This topic is locked and can not be replied to.