E
Eric Mahurin
Some of you might find it interesting that ruby's closures/blocks can
be used as an alternative for defining classes/objects. It's actually
quite simple. You just do this:
- instead of class/def initialize, you use a function/method to define
how to create/initialize an object of that "class" (the function
name).
- add methods by calling define_method w/ a block on the metaclass on
the object that the "class" function will return.
- local variables of the "class" function become "instance" variables
since all of the blocks used for defining methods are closures and
have access to them.
- a variable outside that "class" function that is accessible can be
used a "class variable".
- inheritance is easily done by starting with an object of the base class
- multiple inheritance is probably best done with using a "has-a"
relationship and delegating particular methods to particular member
objects. You could also mixin using Object#extend.
- calling a base class method can be done several ways: a) renaming
the base method before redefining the derived method, b) store the
base method in an instance variable, c) use a "has-a" relationship and
store the base object in an instance variable - inherit/delegate or
define each method explicitly.
All this is mostly academic. You should be able to base a complete
object-oriented language on closures.
There are a few advantages:
- don't need keywords for classes: class, module, self, super, etc.
- don't need to prefix instance/class variable with @ and @@. It is
determined by closure scope instead.
- don't need a special "initialize" method. You put this
functionality at the top-level of the "class" definition instead.
- instance variables are completely private. You could even have base
and derived classes with instance variables of the same name and they
won't collide. Of course you might consider this a disadvantage when
you want a derived class to access an instance variable of a base
class, but this can be done by providing methods to the instance
variables.
Here is a simple example:
class Object
# are we ever going to get the fundamental method built-in to Object?
def metaclass
class<<self;self;end
end
# this would be great to have by default too.
def define_method(name, &block)
self.metaclass.senddefine_method, name, &block)
end
end
# conventional class
class Employee1 < String
@@id = 0
def initialize(name)
super(name)
@id = @@id
@@id += 1
@salary = 0
end
def self.id(); @@id; end
def id(); @id; end
def salary(); @salary; end
def hire(salary); @salary=salary; end
def raise(inc); @salary+=inc; end
def fire(); @salary=0;end
end
# closure "class"
next_id = 0 # class variable: all Employee2 methods/closures share this
Employee2 = proc { |name|
this = String.new(name) # inherits methods from String
id = next_id # instance variable: methods/closures of this object share this
next_id += 1
salary = 0 # instance variable: methods/closures of this object share this
this.define_methodid) { id }
this.define_methodsalary) { salary }
this.define_methodhire) { |s| salary = s }
this.define_methodfire) { salary = 0 }
this.define_methodraise) { |inc| salary += inc }
this
}
Employee2.define_methodid) { next_id } # class method
e1 = Employee1.new("bob")
e2 = Employee1.new("john")
e3 = Employee1.new("sally")
e1.hire(20)==20
e3.hire(25)==25
e1=="bob" and e1.salary==20 and e1.id==0 or raise(e1)
e2=="john" and e2.salary==0 and e2.id==1 or raise(e2)
e3=="sally" and e3.salary==25 and e3.id==2 or raise(e3)
Employee1.id==3 or raise
e1 = Employee2.call("bob")
e2 = Employee2.call("john")
e3 = Employee2.call("sally")
e1.hire(20)==20
e3.hire(25)==25
e1=="bob" and e1.salary==20 and e1.id==0 or raise(e1)
e2=="john" and e2.salary==0 and e2.id==1 or raise(e2)
e3=="sally" and e3.salary==25 and e3.id==2 or raise(e3)
Employee2.id==3 or raise
be used as an alternative for defining classes/objects. It's actually
quite simple. You just do this:
- instead of class/def initialize, you use a function/method to define
how to create/initialize an object of that "class" (the function
name).
- add methods by calling define_method w/ a block on the metaclass on
the object that the "class" function will return.
- local variables of the "class" function become "instance" variables
since all of the blocks used for defining methods are closures and
have access to them.
- a variable outside that "class" function that is accessible can be
used a "class variable".
- inheritance is easily done by starting with an object of the base class
- multiple inheritance is probably best done with using a "has-a"
relationship and delegating particular methods to particular member
objects. You could also mixin using Object#extend.
- calling a base class method can be done several ways: a) renaming
the base method before redefining the derived method, b) store the
base method in an instance variable, c) use a "has-a" relationship and
store the base object in an instance variable - inherit/delegate or
define each method explicitly.
All this is mostly academic. You should be able to base a complete
object-oriented language on closures.
There are a few advantages:
- don't need keywords for classes: class, module, self, super, etc.
- don't need to prefix instance/class variable with @ and @@. It is
determined by closure scope instead.
- don't need a special "initialize" method. You put this
functionality at the top-level of the "class" definition instead.
- instance variables are completely private. You could even have base
and derived classes with instance variables of the same name and they
won't collide. Of course you might consider this a disadvantage when
you want a derived class to access an instance variable of a base
class, but this can be done by providing methods to the instance
variables.
Here is a simple example:
class Object
# are we ever going to get the fundamental method built-in to Object?
def metaclass
class<<self;self;end
end
# this would be great to have by default too.
def define_method(name, &block)
self.metaclass.senddefine_method, name, &block)
end
end
# conventional class
class Employee1 < String
@@id = 0
def initialize(name)
super(name)
@id = @@id
@@id += 1
@salary = 0
end
def self.id(); @@id; end
def id(); @id; end
def salary(); @salary; end
def hire(salary); @salary=salary; end
def raise(inc); @salary+=inc; end
def fire(); @salary=0;end
end
# closure "class"
next_id = 0 # class variable: all Employee2 methods/closures share this
Employee2 = proc { |name|
this = String.new(name) # inherits methods from String
id = next_id # instance variable: methods/closures of this object share this
next_id += 1
salary = 0 # instance variable: methods/closures of this object share this
this.define_methodid) { id }
this.define_methodsalary) { salary }
this.define_methodhire) { |s| salary = s }
this.define_methodfire) { salary = 0 }
this.define_methodraise) { |inc| salary += inc }
this
}
Employee2.define_methodid) { next_id } # class method
e1 = Employee1.new("bob")
e2 = Employee1.new("john")
e3 = Employee1.new("sally")
e1.hire(20)==20
e3.hire(25)==25
e1=="bob" and e1.salary==20 and e1.id==0 or raise(e1)
e2=="john" and e2.salary==0 and e2.id==1 or raise(e2)
e3=="sally" and e3.salary==25 and e3.id==2 or raise(e3)
Employee1.id==3 or raise
e1 = Employee2.call("bob")
e2 = Employee2.call("john")
e3 = Employee2.call("sally")
e1.hire(20)==20
e3.hire(25)==25
e1=="bob" and e1.salary==20 and e1.id==0 or raise(e1)
e2=="john" and e2.salary==0 and e2.id==1 or raise(e2)
e3=="sally" and e3.salary==25 and e3.id==2 or raise(e3)
Employee2.id==3 or raise