method shelters : another way to resolve class extension conflicts

Post on 02-Jul-2015

2.995 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

A presentation on Rubykaigi11

TRANSCRIPT

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’

top related