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