Robert Klemme
2/17/2006 9:02:00 AM
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