Garret, my initial motivation of futures can be actually found at
slide 41-44. It is the ability to compose/stack some generic behaviors
like retries, sharding, timeouts upon some basic operations without
knowing details of those operations.
I've implemented "retry", "timeout" and "safe" wrappers in erlfu to
see if it is feasible concept. Let's assume you have a DB server which
has a very unstable connection, but you want to make sure to get some
results from that server.
Here's example basic code for fetching results from that server:
fetch(Key, Host, Port) ->
{ok, S} = get_tcp:connect(Host, Port, [line]),
gen_tcp:send(S, ["GET ", Key, "\n\n"]),
{ok, Result} = gen_tcp:recv(S, 0),
list_to_integer(Result).
And you have some sort of server who manages connection to that server:
handle_call({fetch, Key}, #state{host = Host, port = Port} = State) ->
Result = fetch(Key, Host, Port),
{reply, Result, State}.
but since remote server is very unstable there's high probabilty that
gen_server:calls will timeout and the gen_server will block for a long
time. So you rewrite it like this (I know I could use noreply, spawn,
gen_server:reply combo for this, but bear with me):
handle_call({fetch, Key}, #state{host = Host, port = Port} = State) ->
F = future:new(fun() -> fetch(Key, Host, Port) end),
{reply, F, State}.
and make client run F:get() when it needs actual data.
This makes gen_server independent of the unstable remote DB server,
which makes things a bit better. But it's not enough, since connection
to DB server is very unstable, hence you want to add timeouts and
retries, and you can do it easily by rewriting that like this:
handle_call({fetch, Key}, #state{host = Host, port = Port} = State) ->
F = future:new(fun() -> fetch(Key, Host, Port) end),
F2 = future:timeout(F, 5000),
F3 = future:retry(F2, 5),
{reply, F, State}.
It's a two-lines change but it "automagically" makes these requests do
5 retries with 5s timeout each.
This example is a bit artificial, but gives some idea about possible
use of those "futures". Other uses which seems possible are generic
sharding behavior, stats gathering, tracing and logging. Which
essentially gives ability to easily reuse some communication-related
patters with very little code repetition.
P.S.
http://github.com/gleber/erlfu now uses gcproc and resource
projects to make futures garbage collected (with a limitation that it
works only inside one node).