Tuesday, December 9, 2008

Result of ACK: Future

Wikipedia's definition of a Future in programming:
In computer science, futures and promises are closely related constructs used for synchronization in some concurrent programming languages. They both refer to an object that acts as a proxy for a result that is initially not known, usually because the computation of its value has not yet completed.
Yesterday evening a number of Rubyists from Amsterdam came together in the public library with our laptops to do cool computer stuff until the library closed.

Among the topics we discussed were new-ish programming languages such Ioke and Clojure. From here the discussion went to the languages that preceded them, like Self and IO.

I think that at some point someone asked "What can IO do that Ruby can't do?", so we started looking at the list of features of IO. There we discovered futures.

Futures look nifty: create an object that does not yet have a value and then move on and eventually do something with the object when you need its value. It will block if there is no value yet, which makes sense, but as long as you don't need the value, you can just move on with your business. This can be useful to do heavy computations or to initiate asynchronous interaction with a remote API. As long as you don't need the result immediately, a future can be useful.

Then I mentioned Ruby's Thread#value, which was new to people. So a bit of code was created that looked similar to:

# sleeps for 3 seconds, then prints 615
t = Thread.new { sleep 3; 123}
puts t.value * 5
Explicitly creating a Thread and calling its value does the trick, but it is not very elegant. If you read it, you will probably end think, "What is that Thread doing? Oh, there's a #value call there. Thread + #value is a future!". Or you don't make the connection and misunderstand what this code is doing.

How should it look then to be more elegant?
# sleeps for 3 seconds, then prints 615
f = future { sleep 3; 123}
puts f * 5
So, this is way more elegant! You see it is a future, because it says so! If you don't know about futures, there is Wikipedia or Google and you might figure out what it does.

In order to make this happen, we need to define a future method that returns an object that when called returns Thread#value for that future. A proxy object. This leads to the next implementation:
class Proxy
def initialize(&block)
@thread = Thread.new(&block)
end

def method_missing(*args, &block)
@thread.value.send(*args, &block)
end
end

def future(&block)
Proxy.new(&block)
end

f = future { sleep 3; 123}
puts f * 5
We created a Proxy class to do the threading logic and use method_missing to capture method calls, which we re-direct to Thread#value.

This works as long as you don't call methods defined on Object or Kernel, because you get those for free in every class. You need to either avoid using them or get rid of them. There is BlankSlate, but since we were just trying to make our idea work, we just un-defined most methods on the object. Then we had the crazy idea to drop the Proxy object altogether and make Thread function as its own Proxy object. Thread, say bye-bye to your methods!

Here is the final bit of code we produced:
# Simple Future implementation, also known as a Promise in Javascript land.
# Based off an old gist ( https://gist.github.com/Narnach/33661 ) and modernized for Ruby 2.
#
# Wrap a block transparently in a Thread, and redirect any method calls to the `value` of that Thread, aka the return value of a Thread.
# This allows you to create code that runs async but becomes blocking when you try to interact with the result of it.
#
# future = Future.wrap { 1 + 2 }
# future + 3 # => 6
#
# Inherit from BasicObject to not get all the usual cruft and allow _all_ method calls to be proxy'd to the value.
class Future < BasicObject
def initialize(thread)
@thread = thread
end
def self.wrap(&block)
new(::Thread.new(&block))
end
# Allow comparison with the thread value
def ==(other)
@thread.value == other
end
# Allows `respond_to?` to work for method_missing implemented methods. Because all methods are implemented via method_missing, this is rather useful.
def respond_to_missing?(method, include_all=false)
@thread.value.respond_to?(method, include_all)
end
# Dispatch all method calls to the value of the thread.
def method_missing(*args, &block)
@thread.value.public_send(*args, &block)
end
end
view raw future.rb hosted with ❤ by GitHub
describe Future do
it 'should return what the block returns' do
val = Future.wrap { 1 + 2 }
val.should == 3
end
it 'should allow transparent interaction with the return value' do
val = Future.wrap { sleep 0.01; 1 + 2 }
(val + 3).should == 6
end
end
view raw future_spec.rb hosted with ❤ by GitHub
# The snippet that started futures in Ruby
# Written by Wes Oldenbeuving, Erik Terpstra and Remco van 't Veer
# Created at the first meeting of ACK (for lack of a better name),
# the Amsterdam Coders Kollective.
def future(&block)
t = Thread.new(&block)
class << t
(instance_methods - %w[value __send__ __id__]).each do |meth|
eval("undef #{meth}")
end
def method_missing(*args, &block)
value.send(*args,&block)
end
end
t
end
f = future do
sleep 1
123
end
puts "present"
puts f * 5
puts "future"


Then the library closed so we went home, after which I polished it a bit and created a github project for it.

This morning my colleague, Filip Slagter, asked me if there wasn't already a library to do futures in Ruby. After a bit of searching, I found there is one. And it looks nice! Still, I like to re-invent the wheel. It is the best way to learn how something works.