Forum: Ruby recontextualizing a block (looking for deep magic)

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
unknown (Guest)
on 2006-02-16 22:34
(Received via mailing list)
What I'm trying to do probably isn't possible, but maybe someone knows
some
deep magic.

I'd like something like:

  def runit &b
    a = 42
    pnew = 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
argument.
What I'm wondering is if there is any way to 'rebind' or
'recontextualize' a
block?


Phil
Robert K. (Guest)
on 2006-02-16 23:25
(Received via mailing list)
2006/2/16, Phil T. <removed_email_address@domain.invalid>:
>
>   runit { puts "a is: #{a}" }
>
>   #would print:
>   a is: 42
>
> Or course, Proc.new currently doesn't take a Binding object second argument.
> 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.

Often there are alternative approaches, such as providing an argument
to the block that makes certain data accessible. If you have a use
case in mind I'd be glad to try to come up with more suggestions.

Kind regards

robert
Jim F. (Guest)
on 2006-02-16 23:31
(Received via mailing list)
On 2/16/06, Phil T. <removed_email_address@domain.invalid> wrote:
>
>   runit { puts "a is: #{a}" }
>
>   #would print:
>   a is: 42
>
> Or course, Proc.new currently doesn't take a Binding object second argument.
> What I'm wondering is if there is any way to 'rebind' or 'recontextualize' a
> block?

This is interesting. For a while you had me thinking the Proc.new {}
should
rebind, but I couldn't get it to. Seems that the binding is based upon
where the
block is defined inside the file.

This is the closest I could get:

  def runit(b)
    a = 42
    pnew = lambda { eval(b,binding) }
    pnew.call
  end

  runit %q{ puts "a is: #{a}" }
unknown (Guest)
on 2006-02-17 00:41
(Received via mailing list)
In article
<removed_email_address@domain.invalid>,
Jim F.  <removed_email_address@domain.invalid> wrote:
>>     pnew.call
>' a
>> block?
>
>This is interesting. For a while you had me thinking the Proc.new {} should
>rebind, but I couldn't get it to. Seems that the binding is based upon wher=
>e the
>block is defined inside the file.

Right. And 99% of the time that's what you want because they're
closures.
.... but sometimes I find I want the block evaluation to be delayed and
done in
a different binding.

I think a Proc.new that took a binding would be useful sometimes, but
I'm not
hopeful that it could be implemented without mucking around with Ruby's
internals (in C).

>
Yeah, I eventually came up with pretty-much the same thing.  This is
probably
what I'll end up doing since:
  %q{ ... }

doesn't look all that different from the regular block syntax:
    { ... }

....but as this is for a DSL, I'm sure users are going to be thinking
"what the
heck is the %q for?" and I don't blame them.  It hurts the aesthetics.

Phil
unknown (Guest)
on 2006-02-17 01:14
(Received via mailing list)
In article <removed_email_address@domain.invalid>,
Robert K.  <removed_email_address@domain.invalid> wrote:
>>     pnew.call
>' 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 F. 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
Jacob F. (Guest)
on 2006-02-17 01:44
(Received via mailing list)
On 2/16/06, Phil T. <removed_email_address@domain.invalid> wrote:
>   class Counter < RHDL
>     inputs    :clk, :rst
>     outputs   :count_out
>     variables :count=>0
>
>     define_behavior {
        # body of define_behavior references 'count'
>     }
>   end

Recapping, your stated problem is that you need access to count in the
block for define_behavior.

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

Why not something like this then?

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

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

Pretty much what it comes to is this: anycode you could put outside
the class definition can also go inside the class definition. This
includes local variables. It's just another scope in that manner.
'count' in the example above is a local variable that goes out of
scope when you 'end' the class definition. The variable gets captured
in the block to define_behavior, however, so it remains available
there (and only there), keeping state between invocations of the
block.

Jacob F.
Jacob F. (Guest)
on 2006-02-17 01:51
(Received via mailing list)
(Responding to myself...)

On 2/16/06, Jacob F. <removed_email_address@domain.invalid> wrote:
>   end
I just realized that what define_behavior (on the class) probably does
is store the block which will then be passed to define_behavior (on
the instance) during the default initialize. Each instance has its own
copy of the block and should have it's own state in count. The code I
posted above won't work like that; all instances would share the same
count, obviously not what you wanted. So... nevermind. :)

Jacob F.
Glen (Guest)
on 2006-02-17 02:09
(Received via mailing list)
Phil T. wrote:
>   #would print:
>   a is: 42


Hi, I am not sure I understood your question, and since noone else has
suggested this yet, I assume that it is not what you are looking for.
But I will post it anyway. Does this do what you want?

def runit &b
  a = 42
  pnew = Proc.new(&b)
  pnew.call(a)
end

runit { |a| puts "a is: #{a}" }
Robert K. (Guest)
on 2006-02-17 11:05
(Received via mailing list)
Phil T. wrote:
>>>     pnew =3D Proc.new &b, binding
>>> or 'recontextualize= ' a block?
>
>
>
>         if clk.event and clk == 1
>
> However, notice the 'variables' method.  The idea there is that there
>     count = init
> block gets constructed or evaluated (or whatever we call what happens
>       #....
>     }
>   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...

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

>   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
Pit C. (Guest)
on 2006-02-17 14:18
(Received via mailing list)
Phil T. schrieb:
>           if rst == 1
> ...
>
> 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.
>
> ...

Phil, I don't understand the usage of variables. When should they be
initialized? What should be their scope (class, instance, thread,
other)? Can't you use instance variables with accessor methods?

Regards,
Pit
unknown (Guest)
on 2006-02-17 22:21
(Received via mailing list)
In article <removed_email_address@domain.invalid>,
Robert K. <removed_email_address@domain.invalid> wrote:
>}
I'd rather just go ahead and use the '@'.

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

True, but.... but since this is a DSL it seems to add a bit of clutter.
With
the other 'syntax' shown earlier, the 'initialize' method is generated
automatically so that it takes the inputs and outputs:

  ag = AndGate.new(a,b,result)

that seems kind of nice...

>>     }
>>   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. :-)

....but it could be the least of the evils.

Phil
unknown (Guest)
on 2006-02-17 22:36
(Received via mailing list)
In article <removed_email_address@domain.invalid>,
Pit C.  <removed_email_address@domain.invalid> wrote:
>>         if clk.event and clk == 1
>>
>> ...
>
>Phil, I don't understand the usage of variables. When should they be
>initialized? What should be their scope (class, instance, thread,
>other)? Can't you use instance variables with accessor methods?
>

Well, it's a bit strange in the example above.  Really what I want is
for it to
be method-level, I suppose.  Recall that I showed an alternative where
the user
actually defines the initialize method explicitly:



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


In that scenario count is a variable whose scope is within the
initialize
method.  It can also be referred to within the scope of the block passed
to
define_behavior (define_behavior in that case just assigns the block to
a
@behavior instance variable.)


In the scenario of the code snippet you quote above the idea was to
somehow
have the behavior block 'recontextualized' within the context of a
generated
method:
  def get_behavior
    count = 0
    #define_behavior block stored in @behavior:
    #the following line is not real Ruby:
    recontextualized_proc = Proc.new &@behavior, binding
  end

then later on the proc can be called.

Of course, if we pass a string to define_behavior and store it, then we
can use
the way shown by Jim F. to acheive the same thing.  The count
variable then
gets initialized to a value outside of the behavior block and is
available
within the behavior block.

Phil
Pit C. (Guest)
on 2006-02-21 10:01
(Received via mailing list)
Phil T. schrieb:
>    end
Hi Phil, sorry for the late reply. I had to implement part of the code
to understand why you can't use accessor methods for variables. Accessor
methods for inputs and outputs are no problem because you just read from
the inputs and set the outputs with a method call (<<). The problem with
variables is that you want to be able to set them via assignment, and
for Ruby

   variable = value

never is a method call. You could write

   self.variable = value

or use other methods to set the value of a variable, but with assignment
  you have to use local variables, like you've shown in the explicit
initialize method above.

Regards,
Pit
unknown (Guest)
on 2006-02-23 22:51
(Received via mailing list)
In article <removed_email_address@domain.invalid>,
Pit C.  <removed_email_address@domain.invalid> wrote:
>>       }
>
>never is a method call. You could write
>
>   self.variable = value
>
>or use other methods to set the value of a variable, but with assignment
>  you have to use local variables, like you've shown in the explicit
>initialize method above.
>

Right.  Here's what I finally came up with:

  class AndGate < RHDL
    inputs :a, :b
    outputs :out
    init {
      somevar = 0
      define_behavior {
        out << (a & b)
        puts "somevar is: #{somevar}"
        somevar +=1
      }
    }
  end

So the 'init' method allows a place to 'declare' variables which will
also be
available to the define_behavior block.  The advantage of this over the
explicit initialize is that there is no repetition of information (you'd
have
to pass the inputs and outputs to the initialize method) as I generate
the
initialize method so that given the above description, the AndGate could
be
used as:

  sa = Sig.new(Bit(0))
  sb = Sig.new(Bit(0))
  sout= Sig.new(Bit())

  a = AndGate.new(sa,sb,sout) #initialize was automatically generated

also, the 'init' block is optional.  If you have no variables to
initialize
then you don't need it:

  class AndGate < RHDL
    inputs  :a, :b
    outputs :out
    define_behavior {
      out << (a & b)
    }
  end

BTW: I'm thinking of using '<=' for assignment instead of '<<' since
'<=' has
lower precedence than '<<' and that would allow:
  out <= a & b  #parens not required

I still wish we had a ':=' operator that could be defined that would
have the
same precedense as '='.

Phil
Pit C. (Guest)
on 2006-02-25 10:23
(Received via mailing list)
Phil T. schrieb:
>         puts "somevar is: #{somevar}"
>         somevar +=1
>       }
>     }
>   end

This is a nice looking compromise.

> BTW: I'm thinking of using '<=' for assignment instead of '<<' since '<=' has
> lower precedence than '<<' and that would allow:
>   out <= a & b  #parens not required

I think you know this already, but if you would use the same assignment
operator for variables, you could omit the init method.

Regards,
Pit
unknown (Guest)
on 2006-02-25 11:35
(Received via mailing list)
In article <removed_email_address@domain.invalid>,
Pit C.  <removed_email_address@domain.invalid> wrote:
>>         out << (a & b)
>>   out <= a & b  #parens not required
>
>I think you know this already, but if you would use the same assignment
>operator for variables, you could omit the init method.
>

Right.  I've considered that option, but for now I think it's better to
make
the distinction between variables and signals (other HDLs make the
distinction
as well where variables are considered to change 'immediately' while
signals
change at the end of the block evaluation).

The other change I'm considering is hiding the explicit class
definition, like so:

  AndGate = Circuit {
    inputs :a, :b
    outputs :out
    init {
      somevar = 0
      define_behavior {
        out <= a & b
        puts "somevar is: #{somevar}"
        somevar +=1
      }
    }
  }
  g=AndGate.new(a,b,a_and_b)

Where Circuit is defined something like:

  def Circuit &b
    #...
    klass=Class.new(RHDL) &b
    #...
    klass
  end

That would hide a little more 'Rubyness' behind the DSL curtain.

Phil
Pit C. (Guest)
on 2006-02-25 12:14
(Received via mailing list)
Phil T. schrieb:
>
> The other change I'm considering is hiding the explicit class
> definition, like so:
> ...
> That would hide a little more 'Rubyness' behind the DSL curtain.

If you want to hide even more of Ruby you could try to get rid of
symbols:

   class RHDL
     class << self
       def method_missing(name, *) name end
       def inputs(*args) p [:inputs, *args] end
       def outputs(*args) p [:outputs, *args] end
     end
   end

   Class.new RHDL do
     inputs a, b
     outputs out
   end

   # => [:inputs, :a, :b]
   # => [:outputs, :out]

In reality you'd have to do more than shown here, but I think it could
work.

Regards,
Pit
unknown (Guest)
on 2006-02-25 21:55
(Received via mailing list)
In article <removed_email_address@domain.invalid>,
Pit C.  <removed_email_address@domain.invalid> wrote:
>     class << self
>
>   # => [:inputs, :a, :b]
>   # => [:outputs, :out]
>
>In reality you'd have to do more than shown here, but I think it could work.
>

Oh, I like that!  I think I'll give it a try.

Phil
This topic is locked and can not be replied to.