From: "headius (Charles Nutter)" <headius@...> Date: 2012-12-02T13:07:48+09:00 Subject: [ruby-core:50468] [ruby-trunk - Feature #4085] Refinements and nested methods Issue #4085 has been updated by headius (Charles Nutter). PROBLEM: reflection from within a refined scope Several folks have stated they believe reflection from within a refined scope should reflect the refinements. I do not agree, based on a definition of refinements as scope-local. class Avocado def tasty? true end end module RefineAvocado refine Avocado do def call_tasty method(:tasty?).call end def tasty? false end end end The argument is that method() here should return the refined version of tasty? (the one that returns false). I don't see any justification for this. The "method" method is defined on Object: class Object def method; ... end end At the point where "method" is called, exactly one refinement is active: the RefineAvocado module which adds "check_tasty" and overrides "tasty?". So "method" dispatches to Object#method. Object#method is not refined, since it is not defined within a scope where refinements are active. It does not see refinements active in scopes earlier in the call stack, so it produces a reference to the original "tasty?" method. What logic dictates that Object#method should now always be a refined method that has to inspect the stack? If Object#method did reflect refinements active in the caller's scope, it would make it possible to get the refined method...but it would make it impossible to get the *original* method. And the same change has been proposed for all reflective access...the refinements basically make it impossible to inspect the original class, because they're in the way. Giving Object#method and friends stack powers also makes them impossible to wrap. If you put your own code around Object#method, it will *never* see refinements because your wrapper's scope gets in the way. So if we made these class-hierarchy-related methods reflect refinements, you would no longer be able to safely wrap any of the following methods: Symbol#to_proc Object#send Object#method Object#methods Object#respond_to? Module#instance_method Module#instance_methods Module#public_instance_methods Module#protected_instance_methods Module#private_instance_methods Module#method_defined? Module#public_method_defined? Module#private_method_defined? Module#protected_method_defined? Module#public_class_method Module#private_class_method Module#public_instance_method Module#singleton_methods Module#protected_methods Module#private_methods Module#public_methods defined? logic for methods and super And this may be just a start. Instead, I propose that reflection of refinements be exposed directly. Kernel#active_refinements => array of refinements active in the current scope, similar to Module#nesting Object#refined_method(active_refinements, name) => returns a Method representing the refined method that would be found for the given list of refinements, or nil Object#send_refined(active_refinements, name, *args) => sends the given name + args as though the specified refinements were active I guess what I'm asking is that you consider a world where all of Ruby is implemented in Ruby, without stack-walking or magic, and don't make that world harder to achieve by adding magic to so many core methods. If people want a way to reflect from within a refined scope, give them those tools; don't change the semantics of the existing tools and give them new powers to inspect the call stack. PROBLEM: to_proc and other coercions in relation to refinements Some folks have said they think to_proc should reflect refinements. I also disagree here. class Lemur def scratch puts "itchy itchy" end end module RefineLemur refine Lemur do def scratch "scratchy scratchy" end end end using RefineLemur ary = [Lemur.new, Lemur.new] ary.each &:scratch This last line is essentially the same as &(:scratch.to_proc) to_proc can be defined like this, in Ruby: class Symbol def to_proc return proc {|obj| obj.send self} end end It should be apparent why to_proc should not reflect refinements: the send call it makes does not live within a refined scope. You are getting a new proc in hand with logic to send that symbol to any object it is passed. Refinements don't enter into it. If we return to the definition of refinements as scope-local prepends, there's no way to justify making to_proc reflect refinements. The scope where refinements are active leads up to the to_proc and each calls, but NO FURTHER. The to_proc call operates independent of the refined scope and cannot see refinements. Making it "special", so it can see up the call stack, would mean it can't be wrapped anymore, can't be implemented in Ruby. You are giving superpowers to a single core method that NO RUBY CODE CAN MIMIC. We should not make yet more core class methods that are impossible to wrap or write in Ruby. PROBLEM: scoping at file level, module level, or some other level The main benefit from making refinements file-scoped is simplifying the refined lookup process. If refinements must all be specified at the top level, then we have a clear set of active refinements at any given time. If they're activated within scopes, it can get confusing: using M1 # M1 is active module Blah using M2 # M1, M2 are active? module Bubble using M3 # M1, M2, M3 are active? "string".some_call ... end using M4 # M1, M2, M4 are active? end # M1 is active The overall effect on virtual-hierarchy method lookup is still basically the same. The refined modules are searched in most-recently-used order. The virtual hierarchy for the some_call call above would be: [M3] < [M2] < [M1] < String < Object If we implement it like Shugo's patch, the overlay modules are attached to the call sites as they are encountered, and only those overlays will ever be active at that call site. So I guess I'm saying the file-level requirement simplifies some things, but doesn't really change anything conceptually. PROBLEM: Matz's example showing String refinements active in a refine Array block This example should not work: module M1 refine String do def foo; puts 'foo'; end end module M2 refine Array do "string".foo end end end Within the M2 refinement, only one refinement is active: M2. M2 does live within M1, but M1's refinements are not active within the body of M1, and therefore they should not be active within the body of M2 or the refine Array block. The virtual hierarchy at the point of the "foo" call looks like this because no active refinements affect String: String < Object I would like to hear justification why the "foo" call above should succeed. It does not fit my mental model of refinements. PROBLEM: module_eval Several folks have claimed refinements will only be useful if they can be applied dynamically to module_eval blocks. However, this does not fit any reasonable definition of refinements thus far. A block lives within a scope, and that scope may or may not have refinements active. You cannot change what refinements the block will see in the same way that you can't change what constants it will see. The following does not work: module A B = 1 end A.module_eval { B } # => NameError: uninitialized constant B And the following should not work either: module A refine String do def foo; puts 'foo'; end end end module B using A end B.module_eval { "string".foo } # => NoMethodError The foo call does not appear within a refined scope, and therefore it must never reflect refinements. I think it's just as important for this feature to know definitively when it *won't* happen as when it *will* happen, and it should NEVER be possible to force refinements on Other People's Code. --- More to come as I think about stuff more. Is it possible for me to get access to edit the wiki page? I'd like to try to fill out more details (assuming I've got the details right). ---------------------------------------- Feature #4085: Refinements and nested methods https://bugs.ruby-lang.org/issues/4085#change-34304 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/