An aspect of evaluation scheduling appears in various disciplines under different names. If you are familiar with any of these pairs, focus on your experience with those pairs to understand the topic of this question:
- Forward chaining vs Backward chaining
- Data-driven vs Demand-driven
- Eager vs Lazy
- Publish vs Subscribe
- Reaction vs Observation
- File modification vs Make
- Push vs Pull
Since reactive programming is currently popular, let us standardize the names Reaction vs Observation to represent, respectively, the left and right side of the above pairs. If you are unfamiliar with reactive programming and its relationship to observation, substitute any of the pairs with which you are familiar as the meaning of those terms in what follows.
Scheduling enters as a concern because the order of evaluation of programs may start with system inputs, in the case of reactive evaluation. In the case of observer evaluation, some entity is bound to a system output -- placing it under observation; as a result evaluation begins with the output, placing other entities under observation, constructing a dependency graph. Once that dependency graph is constructed evaluation can commence with the current state of the data, producing the output value. Only then will reaction to changes in the state propagate through the dependency graph to produce real time changes in the observed system output.
There is no point in:
- Scheduling an evaluation that will never affect a system output, nor
- Evaluating a system output that will never be observed.
If a tree falls in a simulated forest and there is no one around to hear it, does it evaluate a sound? Does the physics engine even run the event?
To simplify this a bit more, let's restrict our attention to functional reaction vs observation aka Functional Reactive Programming. Moreover, based on the prior observation about the priority of observation, let's further focus on what is widely called pull functional reactive programming -- but call it simply Functional Observation Programming, as "observation" implies the set up of the reactive dependency graph.
In pure functional programming -- programming without side effects -- modeling input/output is a perennial challenge. However, in pure Functional Observation Programming there is a pure dataflow graph connecting inputs to outputs without any stored data in between, except for cached (aka memoized) values to optimize evaluation. Side effects needn't contaminate the semantics.
Now that we've focused the question, let's add one more consideration:
In mathematics, "functions" are N to 1 mappings. For example, f(x)=x^2 maps 2 to 4 but it also maps -2 to 4. It is a 2:1 mapping. However, if you consider g(x)=sqrt(x) as the inverse of f(x), you'll notice that it isn't an N:1 mapping because there are two "roots" to g(4): 2 and -2. This is called a "relation" rather than a "function" -- there can be more than one output for a given input. However, f(x) is also a "relation" -- it's just a particular type of relation that happens to have only one output. Functions, therefore, are a special class of relations -- those that have at most one valid associated with any given input. One can use an SQL database table to represent a relation:
x | x^2 -2 | 4 +2 | 4
With such a relation table, one can then run a query that asks for all rows in which x=2, and it will return only one row. On the other hand, one can run a query that asks for all rows in which x^2=4, and it will return 2 rows.
Now we can start to talk about "Relational Reactive Programming" or, to use the "pull" simplification above, "Relational Observation Programming".
Since functions are degenerate (N:1) relations and since there are really just two sides to this ubiquitous dual, the most general design would focus on relations (eg. SQL, logic programming, business rules, etc.) and the way they separate this scheduling dual (henceforth ) from the program code.
By attending to relations under observation, reactivity obtains.
Reactive evaluation begins by putting a relational query under observation and ends by removing it from observation.
The ubiquity of this concern and its poor separation from even high level programming languages, such as business rules engines, seems to be a major gap in the discipline of software engineering.
Even in logic programming implementations with incremental tabling (such as XSB) or SQL implementations with incremental view maintenance with integrity constraint satisfaction (such as Oracle) -- placing a relation under observation and removing it from observation seems to be an afterthought, with attendant semantic noise in the code, rather than an integral aspect of its programming paradigm.
Ideally, one should be able to specify a software system in relational terms, without regard to the order of evaluation. Then, as a separate concern, bind system outputs to particular relations that happen to be N:1 -- or "functions", leaving it to the scheduling algorithms as to how to build the executing system as a dependency graph through which data flows in response to changes in the system inputs. Where relations are not N:1, there would exist the potential for parallel execution because of the functional independence of the rows. As is well known and understood for functional programming, certain sub-graphs of the dependency graph, could be evaluated in parallel based on their independence. Resource competition scheduling between all parallel evaluations would proceed based on scheduling algorithms that anticipate which order of evaluation would be most likely to produce the system output(s) in the shortest time.
I've looked and been unable to find a software system dealing with this ubiquitous problem of contamination of concerns in a way that doesn't seem ad hoc.