Skip to content

Instantly share code, notes, and snippets.

@davidbalbert
Last active August 29, 2015 14:13
Show Gist options
  • Save davidbalbert/c588b72b7fd2d60ea1f4 to your computer and use it in GitHub Desktop.
Save davidbalbert/c588b72b7fd2d60ea1f4 to your computer and use it in GitHub Desktop.

This is a partial implementation of Piumarta and Warth's Open, extensible object models paper in Ruby. It is the simpler implementation where bind returns a method rather than a closure that wraps the method and some arbitrary piece of data. It also doesn't implement any method caching. I'm not super happy with the the split between in the "low level" Ruby object system, and the "higher level" system that we're implementing, but I don't have time to do a redesign before publishing today's PotW :).

Running the program will drop you into a shell where you can play with the object model. It's nicer if you have pry installed because then you'll have access to local variables like symbol and s_delegated.

class Obj
attr_reader :vtable
def set_vtable(vtable)
@vtable = vtable
end
def send(name, *args)
method = vtable.bind(name)
method.call(self, *args)
end
end
class Sym < Obj
SYMBOLS = {}
class << self
private :new
def intern(sym, name)
if SYMBOLS.has_key?(name)
SYMBOLS[name]
else
SYMBOLS[name] = new(name)
end
end
end
attr_reader :name, :vtable
def initialize(name)
@vtable = ::SymbolVT
@name = name.freeze
end
def hash
@name.hash
end
def eql(other)
@name.eql(other)
end
def inspect
"#<Sym #{name}>"
end
alias to_s inspect
end
class VTable < Obj
attr_reader :parent, :methods
class << self
def add_method(vt, name, body)
vt.methods[name] = body
end
def delegated(parent)
vt = VTable.new(parent)
vt.set_vtable(VTableVT)
vt
end
def alloc(vt, klass)
o = klass.allocate
o.set_vtable(vt)
o
end
def lookup(vt, name)
if vt.methods.has_key?(name)
vt.methods[name]
elsif parent
vt.parent.send(::Sym.intern(nil, "lookup"), name)
end
end
end
def initialize(parent = nil)
@methods = {}
@parent = parent
end
def set_parent(parent)
@parent = parent
end
def bind(name)
if self == ::VTableVT && name == ::Sym.intern(nil, "lookup")
methods[name]
else
send(::Sym.intern(nil, "lookup"), name)
end
end
end
VTableVT = VTable.new
# Analogy: Class.class == Class
VTableVT.set_vtable(VTableVT)
ObjectVT = VTable.new
# Analogy: Object.class == Class
ObjectVT.set_vtable(VTableVT)
# Analogy: Class.is_a?(Object) == true
VTableVT.set_parent(ObjectVT)
SymbolVT = VTable.delegated(ObjectVT)
s_lookup = Sym.intern(nil, "lookup")
VTable.add_method(VTableVT, s_lookup, ->(vt, name) {
VTable.lookup(vt, name)
})
s_add_method = Sym.intern(nil, "add_method")
VTable.add_method(VTableVT, s_add_method, ->(vt, name, body) {
VTable.add_method(vt, name, body)
})
s_allocate = Sym.intern(nil, "allocate")
VTableVT.send(s_add_method, s_allocate, ->(vt, klass) {
VTable.alloc(vt, klass)
})
s_intern = Sym.intern(nil, "intern")
SymbolVT.send(s_add_method, s_intern, ->(sym, name) {
Sym.intern(sym, name)
})
symbol = SymbolVT.send(s_allocate, Sym)
s_delegated = symbol.send(s_intern, "delegated")
VTableVT.send(s_add_method, s_delegated, ->(vt) {
VTable.delegated(vt)
})
begin
require 'pry'
rescue LoadError
require 'irb'
IRB.start
else
binding.pry
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment