You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: markdown-pages/blog/reactive-analysis.mdx
+26-7Lines changed: 26 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,7 +13,7 @@ Imagine editing a ReScript file and seeing dead code warnings update almost imme
13
13
14
14
This is what we're bringing to ReScript.
15
15
16
-
The static analyzer that powers dead code detection is being rebuilt on a **reactive foundation**. When you add a reference to a function, the "unused" warning vanishes immediately. When you remove the last use of a module, the dead code warning appears right away. The analysis stays in sync with your code, updating in real time.
16
+
The static analyzer that powers dead code detection is being rebuilt on a **reactive foundation**. When you add a reference to a function, the "unused" warning vanishes quickly. When you remove the last use of a module, the dead code warning appears right away. The analysis stays in sync with your code, updating in real time.
17
17
18
18
## Why This Matters
19
19
@@ -32,21 +32,40 @@ Instead of re-analyzing your entire project on every change, the reactive analyz
32
32
33
33
The result: analysis that completes in milliseconds for typical edits, even in large codebases.
34
34
35
+
### A Concrete Update Flow
36
+
37
+
Suppose you have a call graph where `main` calls `helper`, and `helper` calls `format` and `validate`. In your edit, `validate` starts calling `old`.
38
+
39
+
With batch analysis, the analyzer scans everything again. With reactive analysis:
40
+
41
+
1. The edited `.cmt` updates — **FlatMap** extracts the new reference. The compiler already resolved it: the ref knows its target (`old`) and its source location (inside `validate`)
42
+
2.**Join** maps that source position to its containing declaration, producing a new graph edge: _validate → old_
43
+
3.**Fixpoint** propagates reachability from roots (`main`) along edges — `old` now becomes reachable and enters the live set
44
+
4. A final **Classify** step (a join of declarations with the live set) marks `old` as live — so its dead-code warning disappears
45
+
46
+
The rest of the project graph stays untouched.
47
+
35
48
### A Glimpse Under the Hood
36
49
37
-
The technology behind this comes from [SkipLabs](https://skiplabs.io), whose reactive programming primitives were designed for exactly this kind of incremental computation.
50
+
Here is a simplified view of the reactive dead code pipeline:
51
+
52
+

38
53
39
54
The analysis pipeline is built from composable operators:
40
55
41
56
-**Sources** hold the raw data from your `.cmt` files
42
-
-**FlatMap**transforms and extracts declarationsand references
43
-
-**Join**connects references to their target declarations
44
-
-**Union**combines data from different sources
45
-
-**Fixpoint** computes transitive closures—which functions call which
57
+
-**FlatMap** extracts declarations, references, and annotations from each file's data
58
+
-**Union**merges collections — for example, combining value references, type references, and exception references into a unified set
59
+
-**Join**maps data across collections — for example, mapping each reference to the declaration that contains it, or partitioning declarations into dead and live using the fixpoint result
60
+
-**Fixpoint** computes transitive reachability — starting from root declarations (entry points, annotated `@live`, or externally referenced), following edges to find everything that's live
46
61
47
62
That last one, **fixpoint**, is particularly interesting. Dead code detection needs to know which declarations are _reachable_ from your entry points. This is a classic graph traversal—but doing it incrementally is hard. When you add one edge to a graph, how do you efficiently update the set of reachable nodes without recomputing from scratch?
48
63
49
-
The reactive fixpoint operator solves exactly this problem. It maintains the reachable set incrementally, handling both additions and removals efficiently. This is what makes the analysis fast enough for real-time use.
64
+
In the running example above, only one new edge (`validate → old`) is added, and fixpoint propagates liveness from that delta.
The reactive fixpoint operator solves exactly this problem. It takes a set of roots and a set of edges, and maintains the full reachable set. In this example, once `old` becomes reachable through `validate`, fixpoint only propagates through neighbors affected by that new edge, without revisiting unrelated parts of the graph. This is what makes the analysis fast enough for real-time use.
0 commit comments