r/learncsharp • u/rust-cohle_ • 1h ago
C#'s rules for top-level statements turned out to be a great fit for a reactive notebook, so I built one
been working on something called Reverb, it's basically a notebook for C# where changing one cell automatically re-runs only the cells that depend on it.
the cool thing is that C# already handles a lot of the hard parts for us.
it's just a normal `.cs` file with `// %%` comments separating cells, and you run it with `dotnet run`.
one thing i found interesting is how top-level statements in C# make dependency ordering pretty straightforward. you can't declare the same variable twice, and you can't use a variable before declaring it. so the order of cells in the file is already a valid execution order. no need to build a separate dependency scheduler like reactive Python notebooks such as marimo have to.
there are still a few things that need to be checked though:
- a cell can't assign to a variable declared in another cell.
- anonymous types can't cross cell boundaries because the generated code needs to reference the type.
- local functions were an interesting one. turns out C# lets you call a local function before it's declared, which breaks the whole file-order-is-dependency-order assumption. had to add a rule for that too.
for dependency tracking, i'm using Roslyn's `SemanticModel.AnalyzeDataFlow` on each cell's statements. it tells us which variables are coming from other cells and which ones are declared by the current cell.
each cell then gets compiled into a static method. inputs are read from a shared value store and outputs are written back to it.
also added `#line` directives so compiler errors and stack traces actually point to the right lines in the original file.
here's a small example:
```csharp
// %% id=threshold
var threshold = Ui.Slider("Minimum order", min: 0, max: 1000, value: 400);
// %% id=summary
var big = orders.Where(o => o.Total >= threshold.Value).ToList();
big.Count.Dump("Orders above threshold");
```
move the slider and only the `summary` cell runs again.
install it:
```bash
dotnet tool install -g Reverb.Notebooks
```
it's open source (MIT):
https://github.com/muneeb-devp/reverb
would love to hear about any weird C# data flow edge cases i might have missed. especially things that could break the dependency tracking. always possible i've overlooked something :)
accidently created ama