What is cool about the ChronoGraph? Part #1 - Immutability
Tags: chronograph, reactive, immutability, typescript
Published: 2020-07-08 12:54:06

So how [ChronoGraph](https://github.com/bryntum/chronograph) is different from other reactive state management systems and why you should care?

ChronoGraph introduces novel and unique features, not available in other systems of the same class.

In this post, we'll talk about the most important ones  -  transaction [rejection](https://bryntum.github.io/chronograph/docs/classes/_src_replica_replica_.replica.html#reject), [undo/redo](https://bryntum.github.io/chronograph/docs/classes/_src_replica_replica_.replica.html#undo) and data [branching](https://bryntum.github.io/chronograph/docs/classes/_src_replica_replica_.replica.html#branch). All are based on the fact, that internally, ChronoGraph is a single huge immutable data structure.

Immutability allows us to "travel in time" along the history of the graph  -  that is transaction rejection and undo/redo. 

It also allows create "alternative history"  -  that is data branching.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/6iqatzrv89d4z71lfnjs.png)

The applications of these features are obvious. For example the transaction rejection corresponds to a "Cancel" button of a dialog window. And undo/redo corresponds, well, to undo/redo! :)

Code-wise, "traveling in time" looks like the following snippet. Basically the transactions borders are marked with the [commit](https://bryntum.github.io/chronograph/docs/classes/_src_replica_replica_.replica.html#commit) call and then other calls like [reject](https://bryntum.github.io/chronograph/docs/classes/_src_replica_replica_.replica.html#reject) moves the state "in time".

```ts
import { ChronoGraph } from "../../src/chrono/Graph.js"

const graph1 : ChronoGraph   = ChronoGraph.new({ historyLimit : 2 })

const var1      = graph1.variable(0)

graph1.read(var1) // 0

graph1.commit()

//---- Rejection ----
graph1.write(var1, 10)

graph1.read(var1) // 10

graph1.reject()

graph1.read(var1) // 0

//---- Undo ----
graph1.write(var1, 10)

graph1.read(var1) // 10

graph1.commit()

graph1.undo()

graph1.read(var1) // 0

//---- Redo ----
graph1.redo()

graph1.read(var1) // 10
```

Data branching ("alternative history") is useful for the cases when you want to perform some computation, not affecting the current data state. It can answer questions about the data like - "_What if_ I do XXX with data?"

To use this feature, you create a new "branch" of the graph. One can also think about it as creating a clone. Thanks to immutability this operation is efficient and does not require actual duplication of all data. After the branch is created, all writes to it will not affect the source graph (and vice-versa). The branches can not be merged back currently.

Code-wise it looks like this:
```ts
import { ChronoGraph } from "../../src/chrono/Graph.js"

const graph1 : ChronoGraph   = ChronoGraph.new({ historyLimit : 2 })

const var1      = graph1.variable(0)

graph1.read(var1) // 0

graph1.commit()

//---- Write to branch ----
const graph2 = graph1.branch()

graph2.write(var1, 10)

graph1.read(var1) // 0
graph2.read(var1) // 10

//---- write to source ----
graph1.write(var1, 5)

graph1.read(var1) // 5
graph2.read(var1) // 10
```

Having these powerful features built-in, relieves the programmer from the burden of writing custom solution and ensures they will operate with the best performance.

Do you support reject/undo/redo in your application? If so, how you do that?

P.S. The cover photo of beautiful antique chronograph from 1610, by Andy Currey (used with author permission). Original: https://flic.kr/p/H7Caz3

P.P.S. [Part #2 of this series](https://dev.to/chronograph/what-is-cool-about-the-chronograph-part-2-unlimited-stack-depth-gk4)