In article <9e3fd2c80602161324n4310a4ecx / mail.gmail.com>,
Robert Klemme  <shortcutter / googlemail.com> wrote:
>2006/2/16, Phil Tomson <ptkwt / aracnet.com>:
>> What I'm trying to do probably isn't possible, but maybe someone knows so=
>me
>> deep magic.
>>
>> I'd like something like:
>>
>>   def runit &b
>>     a =3D 42
>>     pnew =3D Proc.new &b, binding
>>     pnew.call
>>   end
>>
>>   runit { puts "a is: #{a}" }
>>
>>   #would print:
>>   a is: 42
>>
>> Or course, Proc.new currently doesn't take a Binding object second argume=
>nt.
>> What I'm wondering is if there is any way to 'rebind' or 'recontextualize=
>' a
>> block?
>
>There is one way: you can use instance_eval with a block. This
>essentially just rebinds self but this is good enough often.
>
>Your example (even if the rebinding part worked) would likely suffer
>from a different problem: a is not known as local variable in the
>block so the compilation would probably not create code that reads the
>local variable a.

Right, that's why instance_eval doesn't work for this case either.

>
>Often there are alternative approaches, such as providing an argument
>to the block that makes certain data accessible. 

True.  And that might be a possibility.

> If you have a use
>case in mind I'd be glad to try to come up with more suggestions.

Mainly I'm trying to make a DSL look prettier and reduce duplication.

The DSL is for simulating chip designs (RHDL).

I was going to have the user define a circuit, or logic gate, like so:

  class AndGate < RHDL
    inputs   :a, :b
    outputs  :out

    define_behavior { out << a & b }
  end

And that particular gate definition would work, however given a different case:

  class Counter < RHDL
    inputs    :clk, :rst
    outputs   :count_out
    variables :count=>0

    define_behavior { 
      process(clk) {
        if clk.event and clk == 1
          if rst == 1
            count = 0
          else
            count += 1
          end
          count_out << count
        end
      }
    }
  end

Now we have a problem, but I'll have to explain a lot before I can get to 
that...

The 'inputs' and 'outputs' methods create accessors for instance vars so that 
in the define_behavior block with follows the user can refer to what will be 
instance variables without needing to append '@' to them (they're accessed via 
methods).  It's an aesthetics issue: I didn't want the user to have to know 
about instance vars and appanding '@'.  It seems to work well.

However, notice the 'variables' method.  The idea there is that there could be 
some variables used in the define_behavior block (and this was the case with 
the previous incarnation of RHDL, so I didn't want to lose that functionality).
Now by 'variable' here I mean that I want a variable that also has scope 
within the define_behavior block.  The define_behavior block will be called 
many, many times during a simulation and I don't want the variable to be 
re-initialzed on each call.  It's a bit like:


  def counter(init)
    count = init
    lambda { count += 1 }
  end

  count_val = counter(2)
  count_val.call #=> 3
  count_val.call #=> 4...

Ok, so now the issue is that the block being passed to define_method above 
might need to refer to a 'variable' (from the variables list), but the problem 
is that those variables don't exist yet when the block gets constructed or 
evaluated (or whatever we call what happens when '{...}' is encountered).

I could make the user provide arguments to the block as you mentioned:

  class Counter < RHDL
    inputs    :clk, :rst
    outputs   :count_out
    variables :count=>0

    define_behavior {|count|
      #....
    }
  end


But they would be repeating themselves, no? ;-)

So that's why I was trying to dynamically create a method that would have the 
variables in the scope of the method and then the proc bound to that scope.

I suppose another way to do it would be to do:

  class Counter < RHDL
    inputs    :clk, :rst
    outputs   :count_out

    def initialize
      count = 0
      define_behavior {
        #.... do something with count ...
      }  
    end 
  end

This is sort of how RHDL is now.  There's no need for a 'variables' method 
since you just declare your variables in the 'initialize' method.  I was trying 
to get away from the explicit 'initialize' definition.  But maybe it's not 
such a bad thing.  'count' would then be available to the block passed to 
define_behavior.

The other alternative is the one that Jim Freeze mentioned:

  class Counter < RHDL
    inputs    :clk, :rst
    outputs   :count_out
    variables :count=>0, :foo=>42

    define_behavior %q{
      #.... do something with count, foo ...
    }  
  end 

In that case defne_behavior takes a string instead of a block.  That solves the 
problem because the string can be evaluated later in different contexts.
But the '%q' is kind of ugly ;-)

So it's a matter of tradeoffs... The other thing to keep in mind is that I want 
to make it fairly easy to translate this DSL to another language (VHDL).  
Having the explicit 'variables' declaration would probably make that easier 
because it matches  VHDL's semantics& syntax very closely.


Phil