Runners and Serialization
Runners provide a pluggable way to evaluate Squiggle code in different environments.
Users of squiggle-lang JS APIs usually run Squiggle with SqProject
, which can take a runner:
Runner is an object that implements the runner interface, and converts RunParams
(Squiggle module, environment, and evaluated imports) to a RunResult
(the result of the evaluation).
@quri/squiggle-lang
exports a number of built-in runners, which are described below.
Built-in Runners
EmbeddedRunner
This is a legacy runner that runs Squiggle code in the main thread.
Its main downside is that Squiggle execution is synchronous, so it blocks the main thread. This is a problem for web applications, where we want to be able to run Squiggle code without blocking the UI.
WebWorkerRunner
This runner runs Squiggle code in a Web Worker.
This runner is only available in the browser.
Next.js warnings
When used with Turbopack in the latest Next.js, you might see these errors in the console:
As far as we can tell, these errors are benign and do not affect the functionality of the runner.
Performance notes
Starting with Squiggle 0.10.0, this runner (wrapped with PoolRunner
) is used by default in Squiggle React components.
It fixes most of UI freezes that were caused by long-running Squiggle evaluations.
Freezes can still occur in some cases:
- When component rendering is slow, e.g. you try to render hundreds of distribution charts simultaneously
- when the output of the model is large, and serialization/deserialization of outputs itself becomes costly; for example, List.upTo(1,300000) -> sum is fast, while List.upTo(1, 50000) is slow.
NodeWorkerRunner
This runner runs Squiggle code in a Node.js worker.
This runner is only available in Node.js environment.
PoolRunner
This runner runs Squiggle code in a pool of other runners.
It is parameterized by two arguments:
makeRunner
: a function that creates a new runner.maxWorkers
: the maximum number of workers to run in parallel.
If all runners are busy, the pool will block the caller until a runner is available.
Serialization
Reversible Serialization
Most runners, with the exception of EmbeddedRunner
, marshall their outputs back to the main thread in serialized form.
Serializing and deserializing Squiggle outputs is somewhat complicated, because:
- values can reference other values, so we need to serialize the entire value graph (in general, Squiggle values are not trees, DAGs, directed acyclic graphs)
- values can include lambdas, for which we have to serialize their ASTs and captured variables
To do this, we rely on the @quri/serialization package, which does most of the heavy lifting.
Important: serialized values are not compatible across Squiggle versions.
Simple Value Serialization
Full reversible serialization is reliable, but its serialized form is not human-readable.
To address this, we provide a simple serialization format that is human-readable, but not reversible.
This serialization format is available:
- via
Danger.json
andDanger.jsonString
functions in Squiggle language. - via
simpleValueFromAny
function in@quri/squiggle-lang
package JS API.
This serialization format is unstable and generally should not be relied upon for long-term storage.
But it can be useful for debugging or for interacting with LLMs (when you want to show a human-readable summary of a Squiggle value).