This post originated from an RSS feed registered with Java Buzz
by Brian McCallister.
Original Post: Closures: What's the big deal?
Feed Title: Waste of Time
Feed URL: http://kasparov.skife.org/blog/index.rss
Feed Description: A simple waste of time and weblog experiment
Folks get all excited about closures for some reason. If you break away from the closure == anonymous function school, the cool thing about closures is that they let a function have state! To demonstrate, I'll borrow a page from Mr. Dybvig (search for "make-stack") and implement a stack as a function which returns a function, which is passed messages explicitely:
def make_stack(list=[])
lambda do |op, *args|
case
when op == :push
list.push args[0]
when op == :pop
list.pop
else
raise "Illegal Operation"
end
end
end
This snippet defines a function which returns an anonymous function which represents a classical stack data structure. It works:
s = make_stack
s.call :push, "First In"
s.call :push, "First Out"
puts s.call :pop
puts s.call :pop
~>
First Out
First In
The reason this works is because a new list local variable in the make_stack function is created each time make_stack is invoked, and thatlist is referenced by the Proc returned (created via the lambda call).
This, btw, is easily the basis for a (very) small object system:
def make_instance
vars = {}
lambda do |op, *args|
if op == :define
vars[args[0]] = args[1]
else
vars[op].call args
end
end
end
Okay, this technically cheats a bit because I needed Object to respond to :call, so I added
class Object
# "calling" an object returns itself unless subclass (Proc) overrides
def call(*args)
self
end
end
Now, this is a complete toy, and hack, and is atrocious, but it illustrates the idea, which we then use:
b = make_instance
e = make_instance
b.call :define, :talk, lambda { puts "Hello, world"}
e.call :define, :talk, lambda { puts "Goodbye, world"}
b.call :talk
e.call :talk
b.call :define, :name, "Brian"
puts( b.call :name )
~>
Hello, world
Goodbye, world
Brian
But it gets more fun, we want inheritance, right? Let's make a minor change to make_instance so that it supports prototype based inheritance, and add a new special funtion, new_instance
def make_instance(vars={})
lambda do |op, *args|
if op == :define
vars[args[0]] = args[1]
elsif op == :new_instance
make_instance(vars.clone)
else
vars[op].call args
end
end
end
Now we can build up a "base class" and create new instances which inherit what is defined on the base (like JavaScript):
person = make_instance
person.call :define, :talk, lambda { |message| puts message }
k = person.call :new_instance
k.call :talk, "hello, world!"