Your SlideShare is downloading. ×
0
Classboxes, nested methods,
and real private methods
Shugo Maeda
2010-11-12
Self introduction
Shugo Maeda
A Ruby committer
A director of Network Applied Communication
Laboratory Ltd. (NaCl)
The co-c...
Where am I from?
Matsue, Shimane, Japan
A sister city of New Orleans
02 77
We are different
03 77
Please accept us
04 77
Topics
were supposed to be:
classboxes,
nested methods,
and real private methods
But...
05 77
Topic
A new feature "Refinements"
06 77
What are Classboxes?
A way to extend classes
07 77
How to extend classes in Ruby?
Subclassing
Mix-in
Singleton methods
Open classes
08 77
Subclassing
class Person
attr_accessor :name
end
class Employee < Person
attr_accessor :monthly_salary
end
09 77
Aspects of subclassing
Normal single inheritance
Subclassing affects only instances of the
subclasses
Implementation-only ...
LSP
Liskov Substitution Principle
An instance of a subtype must behave like an
instance of the supertype of the subtype
An...
An example of LSP
def print_name(person)
puts person.name
end
shugo = Person.new
shugo.name = "Shugo Maeda"
print_name(shu...
A typical LSP violation
class Rectangle
attr_accessor :width, :height
end
class Square < Rectangle
def set_size(x) @height...
A Ruby-specific LSP violation
class Employee < Person
undef name
end
def print_name(person)
puts person.name
end
matz = Em...
Subclassing != Subtyping
Implementation-only inheritance
Duck typing
15 77
Mix-in
class; Stream; ... end
module Readable; ... end
module Writable; ... end
class ReadStream < Stream
include Readable...
Aspects of mix-in
Limited multiple inheritance
Only modules can be multiply inherited
A module has no instances
Modules ar...
Singleton methods
matz = Person.new
def matz.design_ruby
...
end
matz.design_ruby
shugo = Person.new
shugo.design_ruby #=>...
Aspects of singleton methods
Clients of a class can extend the behavior of an
instance of the class
A singleton method def...
Open classes
# reopen Person, and add code
class Person
attr_accessor :age
end
shugo = Person.new
shugo.name = "Shugo Maed...
Aspects of open classes
Clients of a class can extend the behavior of
instances of the class
Classes are extended globally...
Applications of open classes
Ruby on Rails
jcode / mathn
22 77
Ruby on Rails
A good example of monky patching
23 77
Jealous of DHH not for...
24 77
But for ActiveSupport
DHH can extend Ruby without permissions
from Matz
# This doesn't work on plain Ruby!
puts 3.days.ago...
Rails Plugins
Plugins extend framework classes by monky
patching
Using alias to call original methods
alias_method_chain
2...
jcode / mathn
jcode
Adds and refines methods of String for Japanese
character handling
mathn
Provides mathematically natur...
LSP and open classes
s/subtype/class after reopen/g
s/supertype/class before reopen/g
Instances of a class after a reopen ...
an LSP violation
p 1 / 2 #=> 0
require "mathn"
p 1 / 2 #=> (1/2)
29 77
Summary
Subclassing not by clients
Mix-in not by clients
Singleton methods per object
Open classes global
30 77
Extensibility and Modularity
Subclassing, mix-in, and singleton methods are
less extensible
Open classes are less modular
...
What we need
Class extensions
by clients
per class
local
32 77
Possible solutions
selector namespace
Classboxes
33 77
selector namespace
Implemented in SmallScript and ECMAScript 4
A namespace of method names (selectors)
A namespace can be ...
Classboxes
Implemented in Squeak and Java
A classbox is a module where classes are
defined and/or extended
A classbox can ...
An example of Classbox/J
package Foo;
public class Foo { ... }
package Bar;
import Foo;
refine Foo { public void bar() { ....
An example of local rebinding
package Foo;
public class Foo {
public void bar() { System.out.println("original"); }
public...
Is local rebinding needed?
Local rebinding is less modular
Callees might expect the original behavior
Learn from the histo...
Refinements
A newly implemented feature of Ruby
Not merged into the official Ruby repository
Refinements of classes are de...
An example of Refinements
module MathN
refine Fixnum do
def /(other) quo(other) end
end
end
class Foo
using MathN
def bar
...
Demo
41 77
Module#refine
refine(klass, &block)
Additional or overriding methods of klass are
defined in block
a set of such methods i...
Class local refinements
class Foo
refine Fixnum do
def /(other) quo(other) end
end
def bar
p 1 / 2 #=> (1/2)
end
end
p 1 /...
Kernel#using
using(mod)
using imports refinements defined in mod
Refinements are activated only in a file,
module, class, ...
An example of using
using A # A is activated in this file
module Foo
using B # B is activated in Foo (including Foo::Bar)
...
Module#using
using(mod)
Module#using overrides Kernel#using
The basic behavior is the same as Kernel#using
Besides, Module...
An example of Module#using
module A; refine(X) { ... } end
module B; refine(X) { ... } end
class Foo; using A end
class Fo...
using and include
module A; refine(X) { ... } end
module Foo; using A end
class Bar
include Foo
# include does not activat...
Precedence of refinements
Refinements imported in subclasses have
higher precedence
Later imported refinements have higher...
An example of precedence
class Foo; end
module Bar; refine Foo do end end
module Baz; refine Foo do end end
class Quux < F...
Using original features
super in a refined method invokes the original
method, if any
If there is a method with the same n...
An example of super
module FloorExtension
refine Float do
def floor(d=nil)
if d
x = 10 ** d
return (self * x).floor.to_f /...
special eval
Refinements are also activated in
instance_eval, module_eval, and class_eval
53 77
An example of special eval
class Foo
using MathN
end
Foo.class_eval do
p 1 / 2 #=> (1/2)
end
Foo.new.instance_eval do
p 1 ...
Compatibility
No syntax extensions
No new keywords
The behavior of code without refinements never
change
However, if exist...
Applications of refinements
Refinements of built-in classes
Internal DSLs
Nested methods
56 77
Refinements of built-in classes
Refinements are activated in particular scopes
So you can violate LSP like MathN
Refinemen...
Example
class ApplicationController < ActionController::Base
using ActiveSupport::All
protect_from_forgery
end
class Artic...
Internal DSLs
Methods for DSLs need not be available
outside DSLs
So these methods can be defined in refinements
instance_...
Example
module Expectations
refine Object do
def should ... end
...
end
end
def it(msg, &block)
Expectations.module_eval(&...
Nested methods
def fact(n)
# fact_iter is defined in refinements
# available only in fact
def fact_iter(product, counter, ...
Benchmark
make benchmark (5 times)
Environment
CPU: Intel Core 2 Duo U7600 1.2GHz
RAM: 2GB
OS: Linux 2.6.34 (Ubuntu 10.04)...
Additional benchmarks
For refinements
bm_ref_factorial.rb
bm_ref_fib.rb
For nested methods
bm_ref_factorial2.rb
63 77
bm_ref_factorial.rb
if defined?(using)
module Fact
refine Integer do
def fact ... end
end
end
using Fact
else
class Intege...
Benchmark result
Average 2.5% slower than the original Ruby
65 77
Samples
66 77
Considerations
Should include and using be integrated?
Should singleton methods be refinable?
Should modules be refinable?...
Should include and using be
integrated?
Currently they are independent
module Foo
refine Bar { ... }
...
end
module Baz
# ...
Workaround
module Foo
refine Bar { ... }
def used(klass)
klass.send(:include, self)
end
end
module Baz
# using invokes use...
Should singleton methods be
refinable?
Currently singleton methods cannot be refined
module Foo
# This doesn't work
refine...
Workaround
module Foo
# This works
refine Bar.singleton_class do
def foo
end
end
end
71 77
Should modules be refinable?
Currently the argument of refine should be a
class
module Foo
end
module Bar
# This doesn't w...
Workaround
You can refine a class which includes the
module
module Foo
end
class Quux
include Foo
end
module Bar
# This wo...
Implementation improvement
It's not my work
ko1 will do it
74 77
Patch
http://shugo.net/tmp/refinement-
r29498-20101109.diff
75 77
Conclusion
Refinements achieve a good balance between
extensibility and modularity
76 77
Thank you
Any questions?
77 77
Upcoming SlideShare
Loading in...5
×

Classboxes, nested methods, and real private methods

6,780

Published on

0 Comments
16 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
6,780
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
67
Comments
0
Likes
16
Embeds 0
No embeds

No notes for slide

Transcript of "Classboxes, nested methods, and real private methods"

  1. 1. Classboxes, nested methods, and real private methods Shugo Maeda 2010-11-12
  2. 2. Self introduction Shugo Maeda A Ruby committer A director of Network Applied Communication Laboratory Ltd. (NaCl) The co-chairperson of the Ruby Association 01 77
  3. 3. Where am I from? Matsue, Shimane, Japan A sister city of New Orleans 02 77
  4. 4. We are different 03 77
  5. 5. Please accept us 04 77
  6. 6. Topics were supposed to be: classboxes, nested methods, and real private methods But... 05 77
  7. 7. Topic A new feature "Refinements" 06 77
  8. 8. What are Classboxes? A way to extend classes 07 77
  9. 9. How to extend classes in Ruby? Subclassing Mix-in Singleton methods Open classes 08 77
  10. 10. Subclassing class Person attr_accessor :name end class Employee < Person attr_accessor :monthly_salary end 09 77
  11. 11. Aspects of subclassing Normal single inheritance Subclassing affects only instances of the subclasses Implementation-only inheritance Violations of LSP 10 77
  12. 12. LSP Liskov Substitution Principle An instance of a subtype must behave like an instance of the supertype of the subtype An instance of the supertype can be substituted with an instance of the subtype 11 77
  13. 13. An example of LSP def print_name(person) puts person.name end shugo = Person.new shugo.name = "Shugo Maeda" print_name(shugo) #=> Shugo Maeda matz = Employee.new matz.name = "Yukihiro Matsumoto" print_name(matz) #=> Yukihiro Matsumoto 12 77
  14. 14. A typical LSP violation class Rectangle attr_accessor :width, :height end class Square < Rectangle def set_size(x) @height = @width = x end alias width= set_size alias height= set_size end def set_size(rect) rect.width = 80; rect.height = 60 end square = Square.new set_size(square) p square.width #=> not 80, but 60! 13 77
  15. 15. A Ruby-specific LSP violation class Employee < Person undef name end def print_name(person) puts person.name end matz = Employee.new matz.name = "Yukihiro Matsumoto" print_name(matz) #=> undefined method `name'... 14 77
  16. 16. Subclassing != Subtyping Implementation-only inheritance Duck typing 15 77
  17. 17. Mix-in class; Stream; ... end module Readable; ... end module Writable; ... end class ReadStream < Stream include Readable end class WriteStream < Stream include Writable end class ReadWriteStream include Writable, Readable end 16 77
  18. 18. Aspects of mix-in Limited multiple inheritance Only modules can be multiply inherited A module has no instances Modules are also used as namespaces for constants 17 77
  19. 19. Singleton methods matz = Person.new def matz.design_ruby ... end matz.design_ruby shugo = Person.new shugo.design_ruby #=> NoMethodError 18 77
  20. 20. Aspects of singleton methods Clients of a class can extend the behavior of an instance of the class A singleton method defines the behavior of only one particular instance Some objects cannot have singleton methods e.g., instances of Integer 19 77
  21. 21. Open classes # reopen Person, and add code class Person attr_accessor :age end shugo = Person.new shugo.name = "Shugo Maeda" shugo.age = 34 20 77
  22. 22. Aspects of open classes Clients of a class can extend the behavior of instances of the class Classes are extended globally 21 77
  23. 23. Applications of open classes Ruby on Rails jcode / mathn 22 77
  24. 24. Ruby on Rails A good example of monky patching 23 77
  25. 25. Jealous of DHH not for... 24 77
  26. 26. But for ActiveSupport DHH can extend Ruby without permissions from Matz # This doesn't work on plain Ruby! puts 3.days.ago 25 77
  27. 27. Rails Plugins Plugins extend framework classes by monky patching Using alias to call original methods alias_method_chain 26 77
  28. 28. jcode / mathn jcode Adds and refines methods of String for Japanese character handling mathn Provides mathematically natural operations on numbers Bad examples of monkey patching 27 77
  29. 29. LSP and open classes s/subtype/class after reopen/g s/supertype/class before reopen/g Instances of a class after a reopen must behave like instances of the class before the reopen 28 77
  30. 30. an LSP violation p 1 / 2 #=> 0 require "mathn" p 1 / 2 #=> (1/2) 29 77
  31. 31. Summary Subclassing not by clients Mix-in not by clients Singleton methods per object Open classes global 30 77
  32. 32. Extensibility and Modularity Subclassing, mix-in, and singleton methods are less extensible Open classes are less modular 31 77
  33. 33. What we need Class extensions by clients per class local 32 77
  34. 34. Possible solutions selector namespace Classboxes 33 77
  35. 35. selector namespace Implemented in SmallScript and ECMAScript 4 A namespace of method names (selectors) A namespace can be imported into other namespaces Lexically scoped 34 77
  36. 36. Classboxes Implemented in Squeak and Java A classbox is a module where classes are defined and/or extended A classbox can be imported into other classboxes Dynamically scoped called local rebinding 35 77
  37. 37. An example of Classbox/J package Foo; public class Foo { ... } package Bar; import Foo; refine Foo { public void bar() { ... } } package Baz; import Bar; public class Baz { public static void main(String[] args) { new Foo().bar(); } } 36 77
  38. 38. An example of local rebinding package Foo; public class Foo { public void bar() { System.out.println("original"); } public void call_bar() { bar(); } } package Bar; import Foo; refine Foo { public void bar() { System.out.println("refined"); } } package Baz; import Bar; public class Baz { public static void main(String[] args) { new Foo().call_bar(); } } 37 77
  39. 39. Is local rebinding needed? Local rebinding is less modular Callees might expect the original behavior Learn from the history of local variable scoping Singleton methods and open classes can be alternatives However, effective scopes are different 38 77
  40. 40. Refinements A newly implemented feature of Ruby Not merged into the official Ruby repository Refinements of classes are defined per module Effective scopes are explicitly specified no local rebinding Classbox/J like syntax 39 77
  41. 41. An example of Refinements module MathN refine Fixnum do def /(other) quo(other) end end end class Foo using MathN def bar p 1 / 2 #=> (1/2) end end p 1 / 2 #=> 0 40 77
  42. 42. Demo 41 77
  43. 43. Module#refine refine(klass, &block) Additional or overriding methods of klass are defined in block a set of such methods is called a refinement Activated only in the receiver module, and scopes where the module is imported by using refine can also be invoked on classes 42 77
  44. 44. Class local refinements class Foo refine Fixnum do def /(other) quo(other) end end def bar p 1 / 2 #=> (1/2) end end p 1 / 2 #=> 0 43 77
  45. 45. Kernel#using using(mod) using imports refinements defined in mod Refinements are activated only in a file, module, class, or method where using is invoked lexically scoped 44 77
  46. 46. An example of using using A # A is activated in this file module Foo using B # B is activated in Foo (including Foo::Bar) class Bar using C # C is activated in Foo::Bar def baz using D # D is activated in this method end end end 45 77
  47. 47. Module#using using(mod) Module#using overrides Kernel#using The basic behavior is the same as Kernel#using Besides, Module#using supports reopen and inheritance 46 77
  48. 48. An example of Module#using module A; refine(X) { ... } end module B; refine(X) { ... } end class Foo; using A end class Foo # A is activated in a reopened definition of Foo end module Bar using B class Baz < Foo # A is activated in a subclass Baz of Foo # A has higher precedence than B end end 47 77
  49. 49. using and include module A; refine(X) { ... } end module Foo; using A end class Bar include Foo # include does not activate A end 48 77
  50. 50. Precedence of refinements Refinements imported in subclasses have higher precedence Later imported refinements have higher precedence Refinements imported in the current class or its superclasses have higher precedence than refinements imported in outer scopes If a refined class has a subclass, methods in the subclass have higher precedence than those in the refinement 49 77
  51. 51. An example of precedence class Foo; end module Bar; refine Foo do end end module Baz; refine Foo do end end class Quux < Foo; end class Quuux using Bar end module Quuuux using Baz class Quuuuux < Quuux def foo # Quux -> Bar -> Baz -> Foo Quux.new.do_something end end end 50 77
  52. 52. Using original features super in a refined method invokes the original method, if any If there is a method with the same name in a previously imported refinements, super invokes the method In a refined method, constants and class variables in the original class is also accessible 51 77
  53. 53. An example of super module FloorExtension refine Float do def floor(d=nil) if d x = 10 ** d return (self * x).floor.to_f / x else return super() end end end end using FloorExtension p 1.234567890.floor #=> 1 p 1.234567890.floor(4) #=> 1.2345 52 77
  54. 54. special eval Refinements are also activated in instance_eval, module_eval, and class_eval 53 77
  55. 55. An example of special eval class Foo using MathN end Foo.class_eval do p 1 / 2 #=> (1/2) end Foo.new.instance_eval do p 1 / 2 #=> (1/2) end 54 77
  56. 56. Compatibility No syntax extensions No new keywords The behavior of code without refinements never change However, if existing code has a method named refine or using, it may cause some problems 55 77
  57. 57. Applications of refinements Refinements of built-in classes Internal DSLs Nested methods 56 77
  58. 58. Refinements of built-in classes Refinements are activated in particular scopes So you can violate LSP like MathN Refinement inheritance is useful for frameworks 57 77
  59. 59. Example class ApplicationController < ActionController::Base using ActiveSupport::All protect_from_forgery end class ArticlesController < ApplicationController def index @articles = Article.where("created_at > ?", 3.days.ago) end end 58 77
  60. 60. Internal DSLs Methods for DSLs need not be available outside DSLs So these methods can be defined in refinements instance_eval and module_eval are useful for DSLs 59 77
  61. 61. Example module Expectations refine Object do def should ... end ... end end def it(msg, &block) Expectations.module_eval(&block) end it "returns 0 for all gutter game" do bowling = Bowling.new 20.times { bowling.hit(0) } bowling.score.should == 0 end 60 77
  62. 62. Nested methods def fact(n) # fact_iter is defined in refinements # available only in fact def fact_iter(product, counter, max_count) if counter > max_count product else fact_iter(counter * product, counter + 1, max_count) end end fact_iter(1, 1, n) end 61 77
  63. 63. Benchmark make benchmark (5 times) Environment CPU: Intel Core 2 Duo U7600 1.2GHz RAM: 2GB OS: Linux 2.6.34 (Ubuntu 10.04) 62 77
  64. 64. Additional benchmarks For refinements bm_ref_factorial.rb bm_ref_fib.rb For nested methods bm_ref_factorial2.rb 63 77
  65. 65. bm_ref_factorial.rb if defined?(using) module Fact refine Integer do def fact ... end end end using Fact else class Integer def fact ... end end end 64 77
  66. 66. Benchmark result Average 2.5% slower than the original Ruby 65 77
  67. 67. Samples 66 77
  68. 68. Considerations Should include and using be integrated? Should singleton methods be refinable? Should modules be refinable? Implementation improvement 67 77
  69. 69. Should include and using be integrated? Currently they are independent module Foo refine Bar { ... } ... end module Baz # Both include and using are needed # to use mix-in and refinements at the same time include Foo using Foo end 68 77
  70. 70. Workaround module Foo refine Bar { ... } def used(klass) klass.send(:include, self) end end module Baz # using invokes used as a hook using Foo end 69 77
  71. 71. Should singleton methods be refinable? Currently singleton methods cannot be refined module Foo # This doesn't work refine Bar do def self.foo end end end 70 77
  72. 72. Workaround module Foo # This works refine Bar.singleton_class do def foo end end end 71 77
  73. 73. Should modules be refinable? Currently the argument of refine should be a class module Foo end module Bar # This doesn't work refine Foo { ... } end 72 77
  74. 74. Workaround You can refine a class which includes the module module Foo end class Quux include Foo end module Bar # This works refine Quux { ... } end 73 77
  75. 75. Implementation improvement It's not my work ko1 will do it 74 77
  76. 76. Patch http://shugo.net/tmp/refinement- r29498-20101109.diff 75 77
  77. 77. Conclusion Refinements achieve a good balance between extensibility and modularity 76 77
  78. 78. Thank you Any questions? 77 77
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×