From: Charles Oliver Nutter Date: 2010-12-04T21:48:00+09:00 Subject: [ruby-core:33568] Re: [Ruby 1.9-Feature#4085][Open] Refinements and nested methods Hello, On Thu, Dec 2, 2010 at 9:42 PM, Yusuke ENDOH wrote: > 2010/11/30 Charles Oliver Nutter : >> The global serial number approach in 1.9 means that any change that >> flip that serial number cause all caches everywhere to invalidate. >> Normally this only happens on method definition or module inclusion, >> which is why defining methods or including modules at runtime is >> strongly discouraged for performance reasons. > > Sorry I'm not sure that I could follow your argument about performance, > so I may miss your point. > > I guess that casual users will execute all refinements immediately after > program is started, like class definition and method definition. > Thus, the global serial number approach will work well for refinement > in main use cases, I think. > In the sense, nested function by using refinements may be a problem. For the main cases, I see it working this way: * Parser sees "using" in a scope * Child scopes will now be parsed as though they are refined * VCALL, FCALL, CALL in child scopes are instead RVCALL, RFCALL, RCALL ** Likely other calls must be replaced/wrapped too, e.g. +=, []=, []+=, and so on. (messy!) * Refined call versions check refinement first before checking metaclass for target method Maybe this helps make it clear why the refinement state of a given scope must never be mutable...we want to be able to limit the extra refinement checks to calls where refinements are active, leaving unrefined calls as they are today. >> And one last case that's a problem: author's intent. If I write a >> block of code that does "1 + 1", I intend for that code to do normal >> Fixnum#+, and I intend for the result to be 2. It should not be >> possible for a caller to change the intent of my code just because I >> passed it in a bock. This has been my argument against using blocks as >> bindings, and it's another argument against instance_eval being able >> to force refinments into "someone else's code". > > This is not a problem, but rather improvement. ��There is already open > class which so often breaks your intent. ��Refinements may also break > your intent, but it is less often and more controllable than open class. Open classes break my intent globally and usually at boot time. Refinement propagation into blocks is much more likely to break my intent at some arbitrary time in the future, since refinements are much more lazily applied than open class modifications. Lazy breakage is always worse than eager breakage. >> Now, some positive reinforcement for "using" being a keyword and >> instance_eval not propagating refinements. > > I'm not against your proposal, but I wonder if it does not make sense > because we can still write: eval("using FooExt") eval is not a concern, because it always creates a new scope. If a "using" is active, those scopes (or at least their child scopes) would be statically marked as "refined", and my requirements are still met. > To address your concern, `using' keyword should have a block: > > ��using FooExt > �� ��# FooExt enabled > ��end > ��# FooExt disabled > > I don't like this syntax because of more indentation, though. I mention this at the bottom of my reply to Shugo...I'm uncomfortable with "using" applying to the current scope and not just to child scopes encountered after it. Once code begins executing against a given scope, that scope's static state (like whether a refinement is active) should never change. Given that "using" occurs only at toplevel or in class/module bodies (i.e., during file load/require), there's not as many concurrency concerns here. There are just a lot of messy issues with "using" applying to the current scope: * Does it affect calls that came before it? * Do multiple "using" directives combine or overwrite each other? * When pre-parsing or pre-compiling (AOT stuff), you would have to walk the whole file looking for "using" to know how to emit compiled results. What we're really talking about with refinements is a way to lexically say "make calls in these scopes do an extra indirection during lookup." Unrefined calls should never have to perform this additional indirection. >> I can try to come up with a concrete example of the problems with the >> current proposal and implementation, but the concurrency cases would >> be difficult to show. > > I might find serious concurrency problem of Shugo's patch, though I'm > not sure that this is what you mean. ��Indeed, we may have to give up > propagating refinements via block. > > ��class C > �� ��def test; p :test; end > ��end > ��module FooExt > �� ��refine(C) { def test; p :foo; end } > ��end > ��module BarExt > �� ��refine(C) { def test; p :bar; end } > ��end > ��f = proc do > �� ��sleep 1 > �� ��C.new.test > ��end > ��FooExt.class_eval(&f) �� �� �� �� �� �� �� �� �� ��#=> :foo (expected) > ��BarExt.class_eval(&f) �� �� �� �� �� �� �� �� �� ��#=> :bar (expected) > ��[ Thread.new { FooExt.class_eval(&f) }, ��#=> :foo (expected) > �� ��Thread.new { BarExt.class_eval(&f) } �� #=> :foo (not expected) > ��].each {|t| t.join } Thank you, this helps illustrate the problems with modifying a block's (formerly) static state. We really must not modify an already-parsed/compiled scope with new refinements. - Charlie