Precompiling eval expressions

What ways are there to pre-compile an “eval” expression which is going to be
used multiple times, so that it does not have to be parsed every time?
This would be similar to how a Regexp object can be used repeatedly.

So far the best I can come up with is to eval Proc.new, although the
resulting object is only able to access objects which exist at the time of
definition:

myexpr = '“The time is #{t}”‘
myproc = eval "Proc.new { #{myexpr} }"
t = 123
myproc.call
=> NameError: undefined local variable or method `t’ for #Object:0x810dcd8

But if it did exist at that time, it is able to access the 'current’
value, not the value at definition time:

t = 0
myexpr = '“The time is #{t}”'
myproc = eval "Proc.new { #{myexpr} }"
t = 123
myproc.call
=> "The time is 123"
t = 456
myproc.call
=> “The time is 456”

(Can anyone point me to a good explanation of what is going on underneath?)

Anyway, the idea is that I want to load in a number of expressions from a
database (for example containing rules for validating fields or providing
default values); those rules may be used many times but only read in once.
Precompiling them also gives an opportunity to catch syntax errors before
they are actually used.

Regards,

Brian.

Like this?

code = “puts ‘foo’”
o = Object.new
o.instance_eval “def bar;#{code};end”
o.bar

– Nikodemus

···

On Thu, 17 Oct 2002, Brian Candler wrote:

database (for example containing rules for validating fields or providing
default values); those rules may be used many times but only read in once.
Precompiling them also gives an opportunity to catch syntax errors before
they are actually used.

Procs are closures: they contain their enclosing environment (ie. as a
pointer to a stack frame). This is cool because it enables you to create
the kinds of functions you need, dynamically:

def adder n
proc { |x| x + n } # The n is part of the environment, see?
end

a = adder 1 # But is actually 1 here
b = adder 2 # and 2 here

a.call(1) # => 1 …hence the different behaviour
b.call(1) # => 3

This storage of context is also why they are expensive, and why you should
pass blocks, instead of procs to methods when you have the choise:

def one # Better, provided that the outside context is not needed
yield
end

def two # Useless proc creation unless you are going to store it somewere
Proc.new.call
end

one { puts “No context” }
two { puts “context” }

If you really want to grok them, I recommend the “Structure and
Interpretation of Computer Programs”, esp. the metacircular evaluator part.
It’s a bloody good book, and it’s online.

Hope this helps,

– Nikodemus

···

On Thu, 17 Oct 2002, Brian Candler wrote:

(Can anyone point me to a good explanation of what is going on underneath?)

Ah, some things falling into place now :slight_smile:

I can add methods to an individual object, rather than to a class; so I get
a sort of private derived class (i.e. o is no longer really an "Object" but
an "Object plus some stuff", although o.type still says "Object")

Would o.bar be correctly termed a "singleton method"? If so, I think that's
one of the few areas where Programming Ruby creates confusion. As far as I
can see, the only use of the term 'Singleton' in the first exposition is for
creating single instances of a class (i.e. the Singleton mixin), and later
on singleton methods are just mentioned in passing as if you knew about them
already...

Thanks,

Brian.

···

On Thu, Oct 17, 2002 at 06:36:02AM +0900, Nikodemus Siivola wrote:

Like this?

code = "puts 'foo'"
o = Object.new
o.instance_eval "def bar;#{code};end"
o.bar

Procs are closures: they contain their enclosing environment (ie. as a
pointer to a stack frame). This is cool because it enables you to create
the kinds of functions you need, dynamically:

I kind-of got that bit already. It's clear that this cannot be a traditional
linear stack; that would destroy the existing data when the stack was
unwound and reused.

So I imagine that the stack must consist of a linked-list of frames, so that
unwinding to a previous point allows the tail end of the stack to remain in
existence; the "stack" would then fork to become a tree.

I was trying to work out whether defining a new variable ('t' in the
example from before) extends the current stack frame, or allocates and links
in a new frame:

           +-------+ +-------+ +-------+ +-------+
           > frame | <--- | frame | <--- | frame | <--- | frame |
           +-------+ +-------+ +-------+ +-------+
                                         "myproc" "t"
                                         defined defined
                                          here here

But very annoyingly, I just tried the failed example from yesterday and it
now works!

myexpr = '"The time is #{t}"'
myproc = eval "Proc.new { #{myexpr} }"
t = 123
myproc.call
  => "The time is 123"

The only way I can get it to fail is:

myexpr = '"The time is #{t}"'
myproc = eval "Proc.new { #{myexpr} }"
myproc.call
  => NameError: undefined local variable or method `t'
t = 123
myproc.call
  => NameError: undefined local variable or method `t'

What's going on in the second case? Have I managed to split the stack frame,
or is there some deferred compilation going on??

Cheers,

Brian.

···

On Thu, Oct 17, 2002 at 06:54:27AM +0900, Nikodemus Siivola wrote:

I suspect that this is caused be the eval, not by the closures.

Try searching the archives: I recall there was some mention of the way eval
binds names some time back, but I didn't pay attention at the time.

  -- Nikodemus

···

On Thu, 17 Oct 2002, Brian Candler wrote:

What's going on in the second case? Have I managed to split the stack frame,
or is there some deferred compilation going on??

What’s going on in the second case? Have I managed to split the stack
frame,
or is there some deferred compilation going on??

I suspect that this is caused be the eval, not by the closures.

Try searching the archives: I recall there was some mention of the way eval
binds names some time back, but I didn’t pay attention at the time.

– Nikodemus

ri Binding
ri bind

Gavin

···

From: “Nikodemus Siivola” tsiivola@cc.hut.fi

On Thu, 17 Oct 2002, Brian Candler wrote: