On Tue, Oct 25, 2011 at 12:17 AM, Joshua Ballanco <jballanc / gmail.com> wrote: > I've had an alternate idea to accomplish something like a compromise, but > I'm still a ways away from having anything like a patch. > The idea is this: make Proc#freeze snapshot all bindings. > Essentially, telling a Proc to freeze would cause all bindings in the proc > to be evaluated and replaced with their value at that moment. These values, > themselves, would then be frozen, and references to the original code blocks > could be released. Requiring an extra method call to accomplish this is > slightly inconvenient, but it would keep Ruby 2.0 "backwards compatible" (in > quotes because really? does anybody sensible actually abuse proc bindings > like this?). The hope would be that eventually everyone would get into the > habit of freezing their procs and eventually this could become the default > behavior. > I have to admit I haven't had a chance to completely work through all the > details of this idea yet. I will say that my ultimate hope is that the > following two methods would be equivalent with the same performance in Ruby > 2.0: > > def traditional > do_stuff... > end > define_method :meta do > do_stuff... > end.freeze Freezing procs would be useful for optimizing the proc/block itself, but it would do nothing for the surrounding method. If we could freeze a proc or binding, such that none of its enclosing state would be mutable (e.g. local variables), then we could "flatten" it into a single array of closed-over variables or eliminate the closure entirely if no surrounding state were accessed. The latter case is common for define_method: a = whatever define_method :blah do # some code that never accesses a end Currently, unless we do a lot of inspection, it's difficult or impossible to make the "blah" method be as overhead-free as a normal method body. If we knew that the block could be frozen (and arguably, define_method should do this by *default* since it's a threading nightmare to have methods that share local variable scopes) we could turn the block body directly into a method body. However, the case I want to fix is more sinister. Another example: def foo a = please_dont_change_my_value yummy do # whatever code end end ... def yummy(&block) eval <<-EOS, block # hahaha! I can view and change your local variables! a = my_evil_new_value EOS end This is bad. A method I call should NEVER be allowed to see my method's local variables unless I explicitly make them available. And it's not just local variables, either. Instance variables, class variables, constants, $~ and $_, and basically any state that surrounds the block is exposed to the called code, which can then do anything with it you could do in the method body itself. Icky. How about this lovely item: system ~/projects/jruby $ jirb irb(main):001:0> def foo irb(main):002:1> blah {} irb(main):003:1> end => nil irb(main):004:0> def blah(&block) irb(main):005:1> eval 'def foo; puts "hello"; end', block irb(main):006:1> end => nil irb(main):007:0> foo => nil irb(main):008:0> foo hello => nil Lovely, yes? - Charlie