Files
xod/docs/guide/execution-model
2017-06-26 16:36:03 +03:00
..
2017-06-26 16:36:03 +03:00

title
title
Execution Model in Detail

Execution Model in Detail

In contrast to conventional programming, XOD is a data flow language rather than a control flow language. That means there is no such thing as an instruction pointer that determines what command will be executed at the next moment. Instead, updates are done in semi-instantaneous transactions in which all data is evaluated simultaneously.

Functional and effect nodes

There is such thing as a function in mathematics. You know many of them:

  • f(x) = sin x is a unary (i.e. it takes a single argument) function that returns the sine of its argument;
  • f(r) = π × is a unary function that returns the area of a circle with given radius;
  • f(x, y) = √(x² + y²) is a binary function (i.e. it takes two arguments) that returns the distance from the origin to the point (x, y);
  • f(v₀, a, t) = v₀ × t + (a × t²) / 2 is a ternary (i.e. it takes three arguments) function that returns the velocity of an object at a particular moment in time.

Functions are great, because they behave very predictably, i.e. if youre computing the area of a circle, it will always be the same for the same radius. It would be nonsense if the computation today differed from that of yesterday, or if the result changed based on other factors like the weather outside. Furthermore, computing the area of a circle with radius 2 cant affect the result of another computation, e.g. sin 2, now or in the future.

Functions are intuitive and understandable pieces that dont have computational side effects.

Note Experienced programmers say that such functions are referrentially transparent, easy to reason about, stateless, idempotent, and pure.

The characteristics of functions make them ideal building blocks for creating complex computations. The results of one function can be the input arguments of another function, which could feed its results into yet another function, etc.

However, a program consisting solely from functions would always do the same thing and produce exactly the same result. It would not depend on user input, real world events, or time. It would not affect the external world or have the ability to display computational results. All because functions cant have side effects. Thus another kind of building block is required.

In XOD, there are two kinds of nodes available. Functional nodes represent functions. Effect nodes serve as interfaces to the external world, time, and memory of past values.

Functional nodes

Functional nodes always have inputs and output pins. All pins have value types. In other words functional nodes never have pins of pulse type.

Output values only depend on the values at input pins. They cant depend on time (unless given as an explicit input value), parameters of the external outside world, or past computations. Given the same set of input values, they always result in the same set of output values.

Some examples of functional nodes are:

Functional nodes affect nothing but their output values. On their own, they cant change the brightness of an LED or the speed of a motor.

Note If youre an advanced Excel user, think about functional nodes as cell formulas.

Effect nodes

Effect nodes can have just inputs, just outputs, or both. Their pins are always the pulse type. Input pulses drive them to perform effects, and output pulses tell us about effects that have taken place.

The result of activating an effect node can be arbitrary, e.g. turn on a lamp, send an SMS, launch a nuke, or store a value for future use.

The node's implementation also decides when to emit an output pulse. For example, a node could pulse when a sensor reading updates, an SMS is received, or a timeout event occurs.

Some examples of effect nodes are:

Effect nodes complement functional nodes so your program can interact with users, the world, and time.

Program life cycle

At any particular moment, a XOD program is either in a transaction or in an idle state.

While idle, the system remains stable, nothing changes. A board can even choose to go to sleep to preserve the battery. Receiving a new pulse from an effect node, e.g. a system clock or sensor, is what makes the program leave the idle state.

A pulse causes the program to enter a new transaction. The pulse flows downstream along links and causes the nodes it hits to update. The process of updating a node is called evaluation in XOD.

At the moment a pulse is emitted, a node can (and in most cases does) set new values on its other ouput pins with value types such as number or boolean.

Evaluation of a node hit by a pulse usually requires computing actual values on another nodes input pins. The values on these pins could depend on the values of upstream nodes outputs, which in turn depend on the values of their upstream nodes, and so on. All these dependencies are resolved by the XOD runtime engine. The final and intermediate values required to evaluate a node are computed atomically in the transaction.

After all nodes affected by a pulse are evaluated the transaction is complete and the system returns to the idle state.

Transaction rules

No external pulses while a transaction is in progress

All transaction prevent any external pulses from occuring while the current transaction is in progress. Such a pulse would be postponed and trigger a new transaction after the current transaction is complete.

To be more precise, external pulses are pushed into a FIFO queue. Once the system is idle, a pulse is popped from the queue and a new transaction is initiated. The transaction completes, the system goes idle, takes the next pulse from the queue, launches a new transaction, etc. until the queue is empty.

Evaluation order

During a transaction, a node is evaluated only after all nodes it depends on via links have been evaluated.

Consider the following example.

Diamond graph

The result node will only be evaluated after both branches are evaluated, despite the fact that they have node chains of different length. You cant know the order in which the branches will be evaluated. It could be M1-M2-M3, M3-M1-M2, M1-M3-M2, or even M1-M2 and M3 in parallel. Furthermore, evaluation of the result node might be postponed until its values are actually required (so-called “lazy evaluation”). The target platform decides.

The only thing that does matter is that a node will be never evaluated with incomplete data.

That is the reason why inputs cant have more than one incoming link. Otherwise, there would be ambiguity if two or more links tried to deliver different values.

Buffering

Effect nodes outputs are buffered when changed. In other words, the outputs keep their most recent value. The data is persistent between transactions. So a node will “see” the buffered value from an old transaction via a link if the node must be evaluated again due to a change in another input's value.

Pro Tip If youre familiar with conventional programming, think of pins and their buffered values as variables. They hold the program state and evolve over time.

The target platform decides whether the values on functional nodes pins will be buffered. Since a functional node's outputs depend only on its inputs, the decision is just a matter of execution speed vs RAM consumption. It has no effect on actual program behavior.

Feedback loops handling

In XOD, link cycles that contain only functional nodes are not allowed. They would lead to deadlocks and hangs.

However, cycles broken by effect nodes are OK. In this case, a new value will be delivered via the feedback link, but the node will “see” it only once it has received a new incoming pulse.

Summary

The program's life cycle can be looked at as a infinite series of transactions that run whenever an external impact occurs. Pulses drive the program. No pulses, no changes.

Transactions are protected from sudden pulses that could change or make ambiguous the order of node evaluation.