Wilson B. wrote:
actually reading the documentation.
This is basically just a Ruby implementation problem. I’m reading
through the Smalltalk ‘Blue Book’ now, and it shows that it is
possible to have blocks that are ‘real’ objects while still offering
very high performance.
This is something we can fix with hard work on the Ruby internals.
Actually, not really. Smalltalk didn’t have eval. The very prospect that
a Ruby block could call eval on any arbitrary text means you can’t, in
the general case, discard a block’s binding (the biggest benefit to ST
block performance was saving yourself closing over the lexical scope
whenever possible) - and either way eval is a general nuisance to
optimise without Dark Magic. In Ruby, IIRC, the performance hit of
constructing the equivalent of a ST full block is worked around by the
duality of “real” and “fake” blocks - the fake ones don’t need being
constructed as objects because their lexical scope is still on the
interpreter stack. Coincidentally, since “real” blocks are always full
blocks in the ST sense of the term, they’re also a yummy inherent memory
(Someone with more intimate knowledge on Ruby internals might correct me
on the above.)
It’s probably possible to apply some heuristics to try to see if this
might happen as an optimization, but Ruby is a far more complex language
than ST (alias, dynamic requires of source files, eval, constant lookup
to name a few features Blue Book ST didn’t have), and reasoning about
behaviour of Ruby code algorithmically is much harder. So, while it is
probably possible, I wouldn’t expect this to happen anytime soon - it’s
a price you pay for the flexibility and metaprogramming convenience.
Personally, I’d be in favour of either being able to optionally disable
eval in the interpreter if that is in fact the language feature that
makes fast clean blocks impossible (it might be only partially guilty)
and making the standard library eval-free to comply with that.
Alternately, introducing keywords / methods to let a programmer manually
declare which blocks do not reference enclosing locals or the enclosing
object to hand-optimize could work also. (This should be done on
creation, discarding a binding -after- the object is created wouldn’t do
anything for performance, even if it would solve the memory leak