
I extended my Futures code to encapsulate Cells (also known as Modifiables, Calculated values, or Dependent values). The syntax is pretty cool; the implementation is something of a hack, but it works as long as you play by the rules. You can more or less write "normal" code for your expressions. When the value of a Cell is retrieved, it is assumed that the current thread may "touch" other Cells/Dependents in the process of calculating its value. I keep a stack of Cells that are calculating themselves on the current thread (accessible through a ThreadLocal). When a Cell is figuring out its value it pushes itself on the stack. When other Cells are "read" for their values, they peek at the top of the stack and add a dependency edge if one does not already exist. By using the stack only immediate dependencies are stored. This is a naïve implementation, serving mostly to demonstrate that the resulting syntax is quite pleasant.
Cells come in two flavors  Dependent (can't update the expression), and Cell (can modify the expression). Cells descend from Futures, which descend from Function0[T].
The big question is...when a cell is marked as "unset", what is the best strategy for recalculation? Currently it is recalculated the next time it is accessed, but that strategy should be pluggable so that recalculation can be "pushed" to an appropriate processor.
Calling the code looks like this:
object FutureTest extends Application {
import Future._;
// Cells can contain arbitrary expressions,
// and those expressions can be altered.
val a = cell(10);
val b = cell(20);
// Formulas calculate their value based on an
// expression, but the expression cannot be changed.
val c = formula(a + b); c.description = "a + b";
val d = cell(a * b  sqr(c)); d.description = "a * b  sqr(c)";
val e = formula(b * c * d); e.description = "b * c * d";
p("initial");
a.update(30); p("a = 30");
b.update(100); p("b = 100");
d.update(sqr(c)  b * 2 + a); d.description = "sqr(c)  b * 2 + a";
p("d formula");
def sqr(i: int) = i * i;
def p(comment: String): unit = {
Console.println(comment + ": ");
p(a,b,c,d,e); Console.println;
}
def p(n: AnyRef*):unit = n.foreach(i => Console.println(i));
}
The output looks like this:
[C:\src]scala2 classpath classes;\scala1\scala\build\quick\library FutureTest
initial:
Cell([10])
Cell([20])
Formula("a + b" [30])
Cell("a * b  sqr(c)" [700])
Formula("b * c * d" [420000])
a = 30:
Cell([30]>2)
Cell([20]>3)
Formula("a + b" 2>[50]>2)
Cell("a * b  sqr(c)" 3>[1900]>1)
Formula("b * c * d" 3>[1900000])
b = 100:
Cell([30]>2)
Cell([100]>3)
Formula("a + b" 2>[130]>2)
Cell("a * b  sqr(c)" 3>[13900]>1)
Formula("b * c * d" 3>[180700000])
d formula:
Cell([30]>2)
Cell([100]>3)
Formula("a + b" 2>[130]>2)
Cell("sqr(c)  b * 2 + a" 3>[16730]>1)
Formula("b * c * d" 3>[217490000])
