Expand description
§query_lang
An incremental computation engine: a database that caches the results of derived queries, tracks what each result was computed from, and recomputes only what a change actually affects.
A compiler runs the same computation over and over as source is edited —
parse a file, resolve its names, type-check its definitions, lower it to IR.
Most edits touch a fraction of that work, yet a naive compiler redoes all of
it on every keystroke. The query model, from Rust’s own salsa and
rust-analyzer, turns each step into a query: a pure function whose result is
memoized and whose dependencies are recorded as it runs. When an input
changes, the engine invalidates only the queries that transitively read it,
and reuses everything else. query-lang is that engine, and nothing more — it
owns no compiler, no IR, no syntax; it is generic over the queries a consumer
defines.
§The model
Three pieces:
- A
Systemis the definition of your queries: aKeytype that names a query, aValuetype it produces, and acomputefunction that derives one from the other. - A
Databaseholds theSystem, stores your inputs, and caches the derived results. It is the engine: you set inputs and get results, and it handles caching, dependency tracking, and invalidation. - A
getresolves a query. Called from application code it returns a result; called from inside acomputeit reads a dependency, and the engine records that edge automatically.
An input is any key whose value you set directly; every
other key is derived through compute. One Key type names both, so a query
reads an input and another query the same way.
§Example
An input string, a query that parses it to a number, and a query that squares that number. Editing the input recomputes the chain; an edit that leaves the parsed number unchanged stops at the parse via early cutoff.
use query_lang::{Database, System, QueryError};
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
enum Key {
Source, // an input: the raw text
Parsed, // = Source parsed as i64
Squared, // = Parsed * Parsed
}
struct Pipeline;
impl System for Pipeline {
type Key = Key;
type Value = i64;
fn compute(&self, db: &Database<Self>, key: &Key) -> Result<i64, QueryError> {
match key {
// `Source` is an input; this default only applies if it was unset.
Key::Source => Ok(0),
Key::Parsed => Ok(db.get(&Key::Source)?),
Key::Squared => {
let n = db.get(&Key::Parsed)?;
Ok(n * n)
}
}
}
}
let mut db = Database::new(Pipeline);
db.set(Key::Source, 12);
assert_eq!(db.get(&Key::Squared)?, 144);
assert_eq!(db.stats().computed, 2); // Source is a set input; Parsed and Squared ran
// Ask again with no edit: a hit, nothing recomputes.
assert_eq!(db.get(&Key::Squared)?, 144);
assert_eq!(db.stats().hits, 1);§Features
std(default) — the standard library. Without it the crate is#![no_std]and needs onlyalloc; the engine itself uses no operating- system facilities either way.serde— derivesserde::SerializeforRevisionandStatsso a database’s version and cache metrics can be logged or inspected.
§Stability
The public surface is frozen and stable as of 1.0.0: it follows Semantic
Versioning, with no breaking changes before 2.0. The full surface, the
semantic guarantees, and the SemVer promise are catalogued in
docs/API.md.
Structs§
- Database
- An incremental query database: the store of inputs and the cache of derived results, with automatic dependency tracking and invalidation.
- Revision
- A monotonic version stamp for the database.
- Stats
- A snapshot of how a
Databaseresolved its queries.
Enums§
- Query
Error - The error returned from
Database::getandSystem::computewhen a query cannot be resolved.
Traits§
- System
- The definition of a query system: how every derived query is computed.