From: "headius (Charles Nutter)" Date: 2012-11-03T02:06:57+09:00 Subject: [ruby-core:48762] [ruby-trunk - Feature #4085] Refinements and nested methods Issue #4085 has been updated by headius (Charles Nutter). Ok, early notes from writing up specs and starting to explore an implementation for JRuby. * Refinements in modules and class hierarchies does not seem like a problem to me yet. * Refinements are "used" in temporal order...methods added before "using" won't see refinements, refinements added after "using" won't be applied. I think this is a good thing, since it allows us to have a one-shot flag for refinements on methods at definition time. * I notice 2.0.0 does not appear to have a performance impact from refinements in a simple case. No idea how refinements interact with cache invalidation yet. Now, the bad: * Months ago when the original proposal came out, I expressed my concern about refinements applying to module_eval and friends. I still strongly object to this behavior. The problems I enumerated then still apply. Because module/class_eval can take any arbitrary block, including blocks of code from distant locations that do not know anything about used refinements, all calls in all blocks would need to check for refinements every time. This is the simple performance/optimization concern. MRI's invalidation mechanisms are simple enough that it may not affect MRI right now...but if invalidation mechanisms like those in JRuby are ever desired in MRI, I don't think this feature can exist. It is also a GROSS security issue on the scale of block-as-binding. If a library can force code within a block to make completely different calls than the author intended, you're going to cause terrible confusion. Say I write some code in a block and pass it to a badly-written or malicious receiver. That receiver module_eval's my block in a refined scope, changing one or more of the calls I wrote. Suddenly my code, tested in my environment, can behave completely differently just by being passed as a block. If there are errors, they'll be errors reporting my code calling some other method I never intended. Look at this code and tell me what method it calls: def my_code(string) your_code { string.to_i(16) } end The answer is that you can't tell me what method it is calling because the your_code method can basically force it to call anything. This is actually *worse* than monkey patching, because after the call to your_code has completed, I have no evidence that a refinement was active. I need to go spelunking in your_code to figure out why my code didn't behave the way it should. This is dynamic application of refinements, which has been hotly debated and which I *thought* was supposed to be removed. I assume it has been left in because it is required to apply refinements "magically" to all-block code like rspec. I do not see this as an excuse to introduce such an unpredictable feature. Thoughts? I am adding rubyspecs and continuing my research. ---------------------------------------- Feature #4085: Refinements and nested methods https://bugs.ruby-lang.org/issues/4085#change-32244 Author: shugo (Shugo Maeda) Status: Assigned Priority: Normal Assignee: matz (Yukihiro Matsumoto) Category: core Target version: 2.0.0 =begin As I said at RubyConf 2010, I'd like to propose a new features called "Refinements." Refinements are similar to Classboxes. However, Refinements doesn't support local rebinding as mentioned later. In this sense, Refinements might be more similar to selector namespaces, but I'm not sure because I have never seen any implementation of selector namespaces. In Refinements, a Ruby module is used as a namespace (or classbox) for class extensions. Such class extensions are called refinements. For example, the following module refines Fixnum. module MathN refine Fixnum do def /(other) quo(other) end end end Module#refine(klass) takes one argument, which is a class to be extended. Module#refine also takes a block, where additional or overriding methods of klass can be defined. In this example, MathN refines Fixnum so that 1 / 2 returns a rational number (1/2) instead of an integer 0. This refinement can be enabled by the method using. class Foo using MathN def foo p 1 / 2 end end f = Foo.new f.foo #=> (1/2) p 1 / 2 In this example, the refinement in MathN is enabled in the definition of Foo. The effective scope of the refinement is the innermost class, module, or method where using is called; however the refinement is not enabled before the call of using. If there is no such class, module, or method, then the effective scope is the file where using is called. Note that refinements are pseudo-lexically scoped. For example, foo.baz prints not "FooExt#bar" but "Foo#bar" in the following code: class Foo def bar puts "Foo#bar" end def baz bar end end module FooExt refine Foo do def bar puts "FooExt#bar" end end end module Quux using FooExt foo = Foo.new foo.bar # => FooExt#bar foo.baz # => Foo#bar end Refinements are also enabled in reopened definitions of classes using refinements and definitions of their subclasses, so they are *pseudo*-lexically scoped. class Foo using MathN end class Foo # MathN is enabled in a reopened definition. p 1 / 2 #=> (1/2) end class Bar < Foo # MathN is enabled in a subclass definition. p 1 / 2 #=> (1/2) end If a module or class is using refinements, they are enabled in module_eval, class_eval, and instance_eval if the receiver is the class or module, or an instance of the class. module A using MathN end class B using MathN end MathN.module_eval do p 1 / 2 #=> (1/2) end A.module_eval do p 1 / 2 #=> (1/2) end B.class_eval do p 1 / 2 #=> (1/2) end B.new.instance_eval do p 1 / 2 #=> (1/2) end Besides refinements, I'd like to propose new behavior of nested methods. Currently, the scope of a nested method is not closed in the outer method. def foo def bar puts "bar" end bar end foo #=> bar bar #=> bar In Ruby, there are no functions, but only methods. So there are no right places where nested methods are defined. However, if refinements are introduced, a refinement enabled only in the outer method would be the right place. For example, the above code is almost equivalent to the following code: def foo klass = self.class m = Module.new { refine klass do def bar puts "bar" end end } using m bar end foo #=> bar bar #=> NoMethodError The attached patch is based on SVN trunk r29837. =end -- http://bugs.ruby-lang.org/