Method Shelters : Another Way to Resolve Class Extension Conflicts
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 # => 10485760
1.day.ago # => Sat Jul 16 16:15:00 +0900 2011
"survival strategy".pluralize #=> "survival strategies"
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]
class Foo
module MathN using MathN
refine Fixnum do def foo
def /(other) p1/2
quo(other) end
end end
end
end f = Foo.new
f.foo #=> (1/2)
p1/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
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
The Problem in Classboxes
Importing overwrites internally used
classes
◦ Importing causes another conflict
The Problem in
Classboxes
Redefines Original
Integer#div Integer
Uses redefined Use original
Integer#div Integer
returns rational
Oops!
returns 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
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
call importee
call
importer
A Code with Method Shelters
shelter :MathN do
class Fixnum # fixed size integer in Ruby
def /(x)
Rational(self,x)
end
end
end
shelter :Average do
class Array
def avg
sum = self.inject(0){|r,i|r+i}
sum / self.size
end
end
hide
import :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 S0
- Obj#m0
for public API
Exposed methods
◦ Visible from importer
◦ Importer can call or
redefine exposed S1
methods
Exposedly import
◦ Transitive importing
◦ Imported methods are
S2
also visible from importer
Hidden chamber S0
- Obj#m1 - Obj#m0
for internally used
methods
Hidden method S1
◦ invisible in importing
shelter
Hiddenly import
S2
◦ Imported methods are not
exposed
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
Global
- Obj#g0
S0
obj.g0()
No Ambiguity
If 2+ methods are found in imported
shelters S3
- C#m0
◦ Error!
S1 S2
- C#m0
S0
Error!
Syntax
I don’t want to edit parse.y
define methods in a block
shelter :ShelterName do
class Foo
def hoge # <- defined in the method shelter
end
end
end
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: 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
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 Module
def shelter_accessor(name)
define_method name do
ivname=get_unique_name(name)
self.instance_variable_get(ivname)
end
define_method( (name.to_s+"=").to_sym) do|val|
ivname= get_unique_name(name)
self.instance_variable_set(ivname,val)
end
end
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