From: ptkwt@... (Phil Tomson) Date: 2006-02-17T08:13:28+09:00 Subject: Re: recontextualizing a block (looking for deep magic) In article <9e3fd2c80602161324n4310a4ecx@mail.gmail.com>, Robert Klemme wrote: >2006/2/16, Phil Tomson : >> 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