[#50466] [ruby-trunk - Bug #7492][Open] Segmentation fault at DL::TestDL#test_call_double on x64 Windows 8 — "phasis68 (Heesob Park)" <phasis@...>
23 messages
2012/12/02
[#59083] [ruby-trunk - Bug #7492] Segmentation fault at DL::TestDL#test_call_double on x64 Windows 8
— "phasis68 (Heesob Park)" <phasis@...>
2013/12/13
[#50483] [IMPORTANT] 2.0.0 release plan — Yusuke Endoh <mame@...>
ALL COMMITTERS SHOULD READ THIS MAIL! コミッタはこのメール読んで!
5 messages
2012/12/02
[#50561] [ruby-trunk - Bug #7513][Open] TracePoint#enable/disable should not cause error — "ko1 (Koichi Sasada)" <redmine@...>
7 messages
2012/12/05
[#50575] [ruby-trunk - Feature #7517][Open] Fixnum::MIN,MAX — "matz (Yukihiro Matsumoto)" <matz@...>
20 messages
2012/12/05
[#50593] [ruby-trunk - Feature #7517] Fixnum::MIN,MAX
— "shyouhei (Shyouhei Urabe)" <shyouhei@...>
2012/12/05
[#50594] Re: [ruby-trunk - Feature #7517] Fixnum::MIN,MAX
— Charles Oliver Nutter <headius@...>
2012/12/05
On Wed, Dec 5, 2012 at 12:24 PM, shyouhei (Shyouhei Urabe)
[#50636] [ruby-trunk - Bug #7528][Open] CSV.== fails to check object type — "SteveW (Stephen Wattam)" <stephenwattam@...>
6 messages
2012/12/06
[#50645] [ruby-trunk - Bug #7530][Open] Concurrent loads fail with mutex errors — "headius (Charles Nutter)" <headius@...>
7 messages
2012/12/06
[#50660] [ruby-trunk - Feature #4085] Refinements and nested methods — "trans (Thomas Sawyer)" <transfire@...>
3 messages
2012/12/07
[#50699] Commit access for Yehuda Katz — Aaron Patterson <tenderlove@...>
Hi,
4 messages
2012/12/08
[#50923] Re: Commit access for Yehuda Katz
— Charles Oliver Nutter <headius@...>
2012/12/16
I will +1 this, unsure if it has happened already (it's "catch up on
[#50733] [ruby-trunk - Bug #7539][Open] Misleading error message "can't convert nil into string" — "connec (Chris Connelly)" <chris@...>
8 messages
2012/12/10
[#50755] Becoming a committer — Charlie Somerville <charlie@...>
Hi ruby-core,
21 messages
2012/12/11
[#50759] Re: Becoming a committer
— Yukihiro Matsumoto <matz@...>
2012/12/11
Hi,
[#50784] Re: Becoming a committer
— Charles Oliver Nutter <headius@...>
2012/12/11
It's really this easy? If so, I'll send over my public key today :)
[#50795] Re: Becoming a committer
— Yukihiro Matsumoto <matz@...>
2012/12/11
Hi,
[#50797] Re: Becoming a committer
— Charles Oliver Nutter <headius@...>
2012/12/11
I guess there's a few things I'd be interested in:
[#50809] Re: Becoming a committer
— SASADA Koichi <ko1@...>
2012/12/12
(2012/12/12 8:55), Charles Oliver Nutter wrote:
[#50815] Re: Becoming a committer
— Charles Oliver Nutter <headius@...>
2012/12/12
On Wed, Dec 12, 2012 at 12:04 AM, SASADA Koichi <ko1@atdot.net> wrote:
[#50816] Re: Becoming a committer
— "NARUSE, Yui" <naruse@...>
2012/12/12
2012/12/12 Charles Oliver Nutter <headius@headius.com>:
[#50817] Re: Becoming a committer
— Charles Oliver Nutter <headius@...>
2012/12/12
On Wed, Dec 12, 2012 at 2:59 AM, NARUSE, Yui <naruse@airemix.jp> wrote:
[#50765] [ruby-trunk - Bug #7544][Open] Accented characters in IRB — cfabianski (Cédric FABIANSKI) <cfabianski@...>
6 messages
2012/12/11
[#50793] [ruby-trunk - Bug #7547][Open] Dir.mktmpdir('~something') tries to expand a profile directory — "jstanley0 (Jeremy Stanley)" <jeremy@...>
4 messages
2012/12/11
[#50810] [ruby-trunk - Feature #7549][Open] A Ruby Design Process — "brixen (Brian Ford)" <brixen@...>
34 messages
2012/12/12
[#50829] [ruby-trunk - Feature #7549] A Ruby Design Process
— "subwindow (Erik Peterson)" <erik@...>
2012/12/12
[#50837] [ruby-trunk - Feature #7549] A Ruby Design Process
— "subwindow (Erik Peterson)" <erik@...>
2012/12/12
[#50867] [ruby-trunk - Bug #7556][Assigned] test error on refinement — "usa (Usaku NAKAMURA)" <usa@...>
14 messages
2012/12/13
[#50900] [ruby-trunk - Bug #7564][Open] r38175 introduces incompatibility — "tenderlovemaking (Aaron Patterson)" <aaron@...>
14 messages
2012/12/14
[#50913] [ruby-trunk - Bug #7568][Open] Yaml fails to encode zero date string. — "anshul (Anshul Khandelwal)" <anshul@...>
5 messages
2012/12/15
[#50920] [ruby-trunk - Bug #7568][Assigned] Yaml fails to encode zero date string.
— "charliesome (Charlie Somerville)" <charlie@...>
2012/12/16
[#50988] Re: [ruby-trunk - Bug #7568][Assigned] Yaml fails to encode zero date string.
— Aaron Patterson <tenderlove@...>
2012/12/19
On Sun, Dec 16, 2012 at 12:53:14PM +0900, charliesome (Charlie Somerville) wrote:
[#51015] 1.9.3 patch level release — Zachary Scott <zachary@...>
I know unak-san was asking about a 1.9.3 patch level release, so I
8 messages
2012/12/20
[#51099] [ruby-trunk - Feature #7612][Open] Enumerators take a proc — "pedz (Perry Smith)" <pedz@...>
4 messages
2012/12/23
[ruby-core:50465] [ruby-trunk - Feature #4085] Refinements and nested methods
From:
"headius (Charles Nutter)" <headius@...>
Date:
2012-12-02 03:14:01 UTC
List:
ruby-core #50465
Issue #4085 has been updated by headius (Charles Nutter).
Trying to think aloud how things should be structured. Hopefully this will be helpful to others. I base this on my understanding of refinements up to this point.
# VIRTUAL HIERARCHY AS A REPRESENTATION OF REFINEMENTS
I may be starting to form a picture of how refinements are structured. I'll try to summarize a bit...let me know what I have wrong.
Given a simple class hierarchy:
class Foo
def hello; puts "hello in Foo"; end
end
class Bar < Foo
def hello; puts "hello in Bar"; super; end
end
We have a hierarchy like this: Bar < Foo < Object
Method lookup for normal unrefined Ruby proceeds against this hierarchy. First Bar's implementation of "hello" is found and called, then its super call finds and calls Foo's implementation of "hello".
Foo.new.hello # sees hierarchy Foo < Object
Bar.new.hello # sees hierarchy Bar < Foo < Object
Now we add a refinement:
module M1
refine Foo do
def hello
puts "before hello in M1"
super
puts "after hello in M1"
end
end
end
The refine call creates an anonymous module associated with the Foo class and containing a single method definition "hello" which acts around the primary "hello" implementation on Foo-related class hierarchies.
For purposes of lookup, a refined call site now sees a different hierarchy. Let's use the refinement and call a method:
using M1
Foo.new.hello # sees hierarchy [M1 refinement] < Foo < Object
Bar.new.hello # sees hierarchy [M1 refinement] < Bar < Foo < Object
Refinements are activated by the "using" method. Conceptually, when a refinement is brought into a scope, it is seen as being between the target object and its class, intercepting lookup at the call site.
So if normal call-site lookup works like this:
1. Get target object's class
2. Search class for method implementation
Refined call-site lookup works like this:
1. Get target object's class
2. Get the refinements that are active for this call site
3. Search the refinements for method implementation
4. Failing that, search the original class for method implementation
# METHOD LOOKUP AGAINST REFINED HIERARCHIES
Where things start to get fuzzy for me is in step (3). I will summarize how I believe lookup is supposed to proceed
For each class in the target class's hierarchy, look for an active refinement for that class. Refinements on descendants will mask refinements on ancestors, but may super to them if both are active. Super from the innermost refinement returns to searching the normal class hierarchy.
In this case, the call-site-specific hierarchy above makes sense. With method tables in place we have:
For the call:
Bar.new.hello
The call site sees a structure like:
Foo
{:hello => Foo's hello}
Bar < Foo
{:hello => Bar's hello}
M1 refinement < Bar
{:hello => M1's hello}
In this case, it does look similar to prepend, but it's a virtual prepend that only lives at certain call sites. The behavior of "super" here starts to be a bit more clear, since M1's hello naturally supers into Bar's hello, even though M1 refines Foo.
Am I correct so far?
If we add a refinement to Bar:
module M2
refine Bar do
def hello; puts "before hello in M2"; super; puts "after hello in M2"; end
end
end
Now if we're using both refinements:
using M1
using M2 # order is important...it defines the virtual hierarchy
Bar.new.hello
The hierarchy seen at the "hello" call site looks like this: [M2 refinement] < [M1 refinement] < Bar < Foo
And the call proceeds as follows:
M2's hello supers to...
M1's hello supers to...
Bar's hello supers to...
Foo's hello
The virtual hierarchy looks this way for two reasons:
* M2 was used after M1, so it appears first in the search
* The classes that M2 and M1 refine both appear in the normal hierarchy, so both get searched
If we use the modules in a different order, we should see the M1 and M2 results reversed, since they are searched in most-recently-used order *ahead* of the hierarchy and independent of its structure:
using M2
using M1
Bar.new.hello
The virtual hierarchy here is: [M1] < [M2] < Bar < Foo
This would mean that the refinements on Foo fire before the refinements on Bar and M1's hello supers into M2's hello (which supers into Bar's hello). This means that the structure of the original hierarchy has no bearing on the ordering of refinements. The virtual hierarchy from refinements is always based on the order in which they are used in a given scope, regardless of what classes they refine.
Still correct?
# REFINEMENTS ARE BOTTOM-HIERARCHY, SCOPE-LOCAL PREPENDS
If so, then it seems like refinements are essentially bottom-of-the-hierarchy prepends, searched for a given call site only if activated in the current scope and if the target object contains a refined class/module in its hierarchy. Conceptually, that's a bit more clear than other definitions so far.
This also explains the terminology "overlay modules" in Shugo's patch. The modules defined by a refinement are "overlaid" at the call site and searched before searching the original class hierarchy.
The process of looking up a normal direct method call at a refined call site, in more detail:
1. Get the target object's class
2. Get the list of refinements active for this call site
3. For each refinement (in order of "using"), look for the refined class or module in the target object's class hierarchy
4. If it appears in the hierarchy, search it for the method name in question
5. If the method exists, use it for the call (and ideally cache it in some way)
6. If the method in question does not appear on any of the refinements, continue searching the normal class hierarchy
Super lookup proceeds in much the same way, starting from the point in the virtual hierarchy where the method appears and looking up the virtual hierarchy from that point.
# CALLS WITHIN REFINED METHODS ARE REFINED
What about other calls within refined methods?
class Chicken
def cluck
puts bok * 3
end
def bok; "bok"; end
end
Refining a method does not force down-stream calls to see it. Only call sites with that refinement active see the method.
module RefineChicken
refine Chicken do
def bok
"buck" + super
end
end
end
using RefineChicken
Chicken.new.cluck # => returns "bokbokbok" because Chicken's primary "cluck" method is not refined.
But if we refine both methods, the "bok" call site is also refined and sees the refined "bok" method:
module RefineChicken2
refine Chicken do
def cluck
puts bok * 2
end
def bok
"buck" + super
end
end
end
using RefineChicken2
Chicken.new.cluck # => "buckbokbuckbok"
The search logic for the "cluck" call sees the following hierarchy: [RefineChicken2] < Chicken < Object
The search logic for the "bok" call sees the same hierarchy, because it is made from within a scope where RefineChicken2 is active.
---
Hopefully this is all correct and describes the basic structure of refinements in a way people can understand. I'll give some thought to the troublesome cases from this thread and see how they should behave.
----------------------------------------
Feature #4085: Refinements and nested methods
https://bugs.ruby-lang.org/issues/4085#change-34302
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/