Upload
s-akai
View
2.999
Download
0
Embed Size (px)
DESCRIPTION
A presentation on Rubykaigi11
Citation preview
Method Shelters : Another Way to Resolve Class Extension Conflicts Classboxes でも Refinements でもない別のやり方
Shumpei Akai / 赤井駿平
@flexfrank
What I talk about today
Open Class causes conflicts of methods
Method Shelters resolve it!
% whoami
Shumpei Akai / 赤井駿平
@flexfrank
Ph.D. Student / 学生(博士課程)
◦ 東工大の千葉研
◦ Programming Languages and Moduralization
◦ プログラミング言語とモジュール化
Today’s topic is my current research
Open Class
Ruby’s one of the important features
You can (re)define methods in existing classes
◦ including Object, Integer, Array, …
Frequently used in Ruby world
in open-uri
open-uri redefines “open” method
◦ It accepts URI
open("http://penguindrum.jp/"){|f|f.read}# => Errno::ENOENT: No such file or directory
require "open-uri”open("http://penguindrum.jp/"){|f|f.read}# => "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\”…”
in Ruby on Rails
ActiveSupport adds various convenient methods to core classes
◦ e.g.
10.megabytes # => 104857601.day.ago # => Sat Jul 16 16:15:00 +0900 2011"survival strategy".pluralize #=> "survival strategies"
Or Monkey Patching
Problem of Open Class
Methods may conflict
When One library adds a method
◦ and another library adds a method with the same name in the same class
The method defined first is vanished!
◦ depends on the order of “require”
◦ Confusing!
Method conflicts
I encountered about five years ago
◦ Ruby on Rails and flvtool added a method to String
◦ they used String as binary/array of int
Difficult to collaborate these two libraries
◦ I separated processes
◦ communicating via dRuby
Another example of conflicts
“mathn” redefines the “Integer#/”
◦ returns a Rational object
◦ Ordinary code expects “/” returns a integer
Programs get broken
Existing Solutions
Several module systems are proposed to resolve conflicts
◦ Selector namespaces (for Smalltalk)
◦ Classboxes (for Smalltalk and Java)
◦ Refinements (for Ruby)
◦ …
Refinements
Proposed by Shugo Maeda [ruby-core:33322]
module MathNrefine Fixnum do
def /(other) quo(other)
endend
end
class Foousing MathNdef foo
p 1 / 2end
end
f = Foo.newf.foo #=> (1/2)p 1 / 2
Refinements (cont.)
Refinements changes behavior of methods in a lexical scope
◦ methods defined by Refinements are not enabled in indirectly called methods
◦ No local rebinding
I need local rebinding
◦ e.g. scoped monkey patching
Classboxes
You might have known via matz’s diary
Classboxes
A classbox restrict the scope of methods
A classbox can import a class in another classbox
◦ You can use an imported class
◦ You can add/redefine methods to the imported class
◦ A redefined method can be called from the imported class
Local rebinding property
Classboxes
Cited from “Classbox/J: Controlling the Scope of Change in Java”
The Problem in Classboxes
Importing overwrites internally used classes
◦ Importing causes another conflict
The Problem in Classboxes
Redefines Integer#div
Original Integer
Uses redefined Integer#div
returns rational
Oops!returns integer
Use original Integer
I need another module system
A new module should:
◦ have local rebinding
◦ provide a way to resolve conflicts cause by importing
◦ not depends on the order of load
Method Shelters
Key concept
Hide your methods
Hide your imports
What is a method shelter
A method shelter is a module which provides a scope of methods
◦ define methods in a method shelter
◦ import other method shelters
You can call methods in the imported shelter
You can call methods in the shelter which is importing the current shelter for local rebinding
importer
importeecall
call
A Code with Method Shelters
shelter :MathN doclass Fixnum # fixed size integer in Ruby
def /(x)Rational(self,x)
endend
endshelter :Average do
class Arraydef avg
sum = self.inject(0){|r,i|r+i}sum / self.size
endendhideimport :MathN
end
What conforms a method shelter
A method shelter is separated into tow parts
◦ An exposed chamber and a hidden chamber
◦ in order to protect internally used methods
◦ Each chamber can define methods and import
- Exposed - Hidden
Exposed Chambers
for public API
Exposed methods◦ Visible from importer
◦ Importer can call or redefine exposed methods
Exposedly import◦ Transitive importing
◦ Imported methods are also visible from importer
- Obj#m0S0
S1
S2
Hidden chamber
for internally used methods
Hidden method
◦ invisible in importing shelter
Hiddenly import
◦ Imported methods are not exposed
- Obj#m1 - Obj#m0S0
S1
S2
Global Methods
Ordinal methods not in shelters
◦ Callable from any shelter
◦ Global methods can call methods in a shelter if the caller is in the shelter
obj.g0()S0
- Obj#g0
Global
No Ambiguity
If 2+ methods are found in imported shelters
◦ Error!
S0
- C#m0S1 S2
Error!
- C#m0S3
Syntax
I don’t want to edit parse.y
define methods in a block
shelter :ShelterName doclass Foodef hoge # <- defined in the method shelterend
endend
Syntax: Import
shelter :ShelterName doimport :AnotherShelterName
end
Syntax: hide
“hide” method switches a chamber
◦ do def or import below “hide”
shelter :ShelterName do# exposed chamber
hide# hidden chamber
end
Evaluate
Evaluate within a shelter
shelter_eval :ShelterName do#shelter is enabled
end
Method Lookup Algorithm
1. look up hidden-chamber
2. look up exposed-chamber
3. look up global methods
If not found, go to superclass
start
Global
1
2
3
4
5
7
8
6
9
Global
Found!
Start
1
Implementation
Based on Ruby 1.9.2
Add one implicit argument to method:
◦ A node of method shelter tree
Optimization: Method Cache
Shelter node cache
◦ Caches method entry in a node of shelter
Extend inline cache
◦ Size of an inline cache : 3 word -> 4word (per method call)
◦ Stores the found shelter node
Performance: empty methods
Call a empty method 10,000,000 times
◦ Less than 5% overhead when shelters are used
Performance: Fibonacci
fib(33) in a method shelter
◦ Up to 20% overhead
Performance: Ruby on Rails
Enabled in an action method
◦ Numeric#/.*bytes?/ methods are in a shelter
In the action
◦ 1. Call one method in shelter
◦ 2. One access to SQLite via ActiveRecord
on WEBRick
Rails3
Performance: Ruby on Rails (result) 4% overhead on production env.
50% on development
◦ Method caches are invalidated per req.production
development
Cache hit ratio on rails
Count shelter’s cache hit
Performance: tDiary 3.0.1
defined String#to_a, String#each, String#method_missing in a shelter
◦ These are used for compatibility of 1.8 & 1.9
Ran on CGI with apache
Method shelter improved performance !!
◦ Why?
Why shelter made tDiary fast
String#method_missing issue
“require” calls its arg’s to_path method if defined
◦ If arg’s method_missing is defined, try to call it
◦ String#method_missing slows “require”
Method shelter restrict its negative effect
Other Usage: protect optimized methods In ruby, + - * / … are optimized
◦ only if they are not redefined
◦ Redefinition slows programs
Method shelters can confine effect of redefinition
Method shelter can improve performance
Other Usage: shelter-private accessor Ruby has no private instance variables
A method shelter can mimic private ivars
◦ by generating unique name
◦ Accessible within the defined shelter
and visible shelters
class Moduledef shelter_accessor(name)define_method name doivname=get_unique_name(name)self.instance_variable_get(ivname)
enddefine_method( (name.to_s+"=").to_sym) do|val|ivname= get_unique_name(name)self.instance_variable_set(ivname,val)
endend
end
Conclusion
Open class is dangerous
Method shelters resolving conflicts
◦ With hidden methods, hiddenly importing
I implemented in Ruby
◦ Not so slow (個人的な感覚)
For more details or the source code,
◦ wait for the acceptance of my paper
Deadline: 2.days.since
Questions?
時間が余ったら
Global
In my lookup algorithm,
◦ Shelter must have up to one parent
For simpler semantics
For efficient implementation
before
A
B
C
after
A
B
C’’C’