Phil Tomson wrote:
> 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? ;-)

And especially assigning to "count" would be useless.

How ugly do you feel about

define_behavior {|in,out,var|
  if in[:rst]
    var[:count]=0
  elsif in[:clk]
    var[:count]+=1
  end
}

This has the added benefits of

 - giving you the choice what to provide as arguments (could be simply
hashes but anything else that supports the interface)

 - introducing separate namespaces for each type of variable

But then again, using plain instance variables might be even simpler and
less cluterrish...

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

IMHO explicit is not bad.  Often it makes code more readable and
maintainable.

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

Yes. :-)

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

Kind regards

    robert