Being able to "compute" a value can only go so far.

The IComputation interface enables property values that are computations rather than static values.

But in GPO we want to be able to place dependencies on such computations - perhaps to be able to use the computed value in a classification for subsequent lookup.

...and in general a computation will itself depend on other property values.

Complexity

Even tho' GPO provides mechanisms to setup and manage such dependencies, in practise it would get very complicated and therefore unreliable as computations themselves become more complex.

EVAL S-expression Pattern

Support for these kind of computations and dependencies is provided by a formula approach. So, for example:

agpo.set("net", 10);

agpo.define("vat", "(* 0.175 (-> net))");
agpo.define("total", "(+ (-> net) (-> vat))");

In the above examples a "scheme like" syntax is used to construct a formula:

(* 2 3 4) => 24

The (-> function performs a lookup and may navigate a number of objects:

anogpo.set("item", agpo);
anogpo.set("item-value", "(-> item total)");

Dataflow

The s-expression based computation structures are all themselves GPO objects, this allows them to use the GPO dependency mechanism to trigger dataflow type computations.

This means that values are recomputed as the values on which they depend are changed - just like in a spreadsheet.

Set-based computations

A key quality of spreadsheets is that they provide formulas for manipulating sets of objects.

A classic example would be =SUM(A12:G12) to sum the values of all the cells in column 12 in rows A -> G.

Since GPO understands explicitly about sets of objects, a similar formula can be provided.

basket.define("net", "(sum basket value)");

Will sum all the values of the "value" property for all objects in the set defined by the "basket" property - basket.getLinkSet("basket").

And can be used in subsequent formulas.

basket.define("vat", "(* 0.175 (-> net))");
basket.define("total", "(+ (-> net) (-> vat))");
basket.define("display", "(str 'Total due : ' (-> total))");

With the following result :

item1.set("value", 10);
item1.set("basket", basket);

basket.get("display");
Total due : 11.75

item2.set("value", 10);
item2.set("basket", basket);

basket.get("display");
Total due : 23.50

item2.set("value", 20);
basket.get("display");
Total due : 35.25