The State Management Problem

Most programming languages provide few tools for managing the state of an application. In general it is up to the programmer to establish listeners for important events or user interactions and manually update the state of the application by modifying memory directly. This is known as imperative programming. There is no way to specify the application state as a set of a components, the states of which, are a function of the states of other components.

Manually tracking and updating the state of the application is tedious as it requires a lot of boilerplate code to be written to listen for changes in the states of various application objects and update the states of all objects whose state depends on those objects. This boilerplate code often can not be abstracted away and has to be rewritten for each application. Furthermore, the application logic is not contained in a central location but is rather dispersed throughout various event listeners. Changes to the application logic require changes in multiple event listeners, which can introduce subtle bugs if the application logic is not implemented correctly across all event listeners. As a result the core application logic is inflexible and difficult to change once it has been written.

The problem is magnified when concurrency is involved. Consider that the state of the user interface needs to be updated in response to an event, such as the arrival of data from the network, however the state of the user interface may only be updated from a specific main thread whereas the event is delivered on a background thread (different from the main thread). Suddenly inter-thread (and sometimes even inter-process) synchronization logic has to be intermixed with the application logic. The net result is that the application logic is obscured in layers of event listener and synchronization code.

Partial Solutions

Some toolkits or frameworks provide functionality for binding various UI elements to application objects, such that when the state of the UI element changes, the state of the application object is automatically updated, without having to write additional code. However, these bindings are usually very limited, as they only support trivial bindings which only allow the value of the application object to be automatically updated to match the value stored in the UI element or vice versa. As an example of a trivial binding, consider a text field bound to an an application object X, when the user enters text in the text field X is automatically set to the value stored in the text field. The binding is unconditional, meaning it is not possible to specify under which conditions the state of the application object should be automatically updated and under which conditions it should not. Arbitrary functions are usually not supported, that is it is not possible to specify, for example, that the state of an application object Sum is bound to the sum of the values of two text fields A and B. The result is that programmers often forgo using bindings, in favor of imperative state management, due to these limitations.

Some toolkits may provide support for bindings involving more complex functions, often known as value transformers or formatters. However, these generally have to be specified in a different language, separate from the language in which the bindings are specified, usually requiring that a subclass or some value transformer interface is implemented, which complicates the matter. Moreover, these are usually still limited to a function of a single argument, that is the state of the component can only be bound to a maximum of one other component, meaning the Sum example cannot be implemented without resorting to lower-level imperative state management.

The Tridash Solution

In Tridash an application is decomposed into a set of separate components, called nodes, which can correspond to UI elements, other stateful components, such as the filesystem, operating system or network, or simply computational components. Each node has a particular value, at a given moment in time, which comprises the node’s state. The value (state) of each node can be specified as an arbitrary function of the values (states) of an arbitrary number of other nodes. This is unlike most toolkits/frameworks, which claim to support bindings, that only allow a simple binding to a single other node to be established. Bindings involving arbitrary functions can be specified, and even new functions can be written, entirely using Tridash without having to drop-down to a lower-level language. The aim is that, using Tridash, the entire application state can be specified as a set of nodes where the state of each node is a function of other nodes.