mirror of
https://github.com/salsa-rs/salsa.git
synced 2024-12-25 05:29:43 +00:00
update readme
This commit is contained in:
parent
17789040fa
commit
ff7177a760
2 changed files with 14 additions and 114 deletions
|
@ -6,6 +6,7 @@ edition = "2018"
|
|||
license = "Apache-2.0/MIT"
|
||||
repository = "https://github.com/nikomatsakis/salsa"
|
||||
description = "A generic framework for on-demand, incrementalized computation"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
derive-new = "0.5.5"
|
||||
|
|
127
README.md
127
README.md
|
@ -16,119 +16,18 @@ Yehuda Katz, and Michael Woerister.
|
|||
|
||||
It tries to hit a few goals:
|
||||
|
||||
- We don't have to have a base crate that declares the "complete set of queries"
|
||||
- Each module only has to know about the queries that it depends on and that it provides (but no others)
|
||||
- Compiles to fast code, with no allocation, dynamic dispatch, etc on the "memoized hit" fast path
|
||||
- Can recover from cycles gracefully (though I didn't really show that)
|
||||
- No need for a base crate that declares the "complete set of queries"
|
||||
- Each query can define its own storage and doesn't have to be memoized
|
||||
- Each module only has to know about the queries that it depends on
|
||||
and that it provides (but no others)
|
||||
- Compiles to fast code, with no allocation, dynamic dispatch, etc on
|
||||
the "memoized hit" fast path
|
||||
- Can recover from cycles gracefully (though I didn't really show
|
||||
that)
|
||||
- Should support arenas and other lifetime-based things without requiring
|
||||
lifetimes everywhere when you're not using them (untested)
|
||||
|
||||
## How to use it
|
||||
|
||||
The way that it is meant to be used is roughly like this:
|
||||
|
||||
### Invoking a query
|
||||
|
||||
You will always be threading around a "query" context value. To invoke
|
||||
a query `foo` on the value `key`, you do:
|
||||
|
||||
```rust
|
||||
query.foo().of(key)
|
||||
```
|
||||
|
||||
The syntax is intended to be extensible, so we can add other methods
|
||||
besides `of` eventually (e.g., you might want methods that potentially
|
||||
recover from a cycle, which `of` does not). We could change this to
|
||||
`query.foo(key)`, which is what rustc does, with minimal hassle.
|
||||
|
||||
### Defining query traits
|
||||
|
||||
Each "major module" X will declare a trait with the queries it
|
||||
provides. This trait should extend the traits for other modules that X
|
||||
relies on. There will eventually be a "central context" that
|
||||
implements all the traits for all the modules.
|
||||
|
||||
So, for example, a type checker module might declare:
|
||||
|
||||
```rust
|
||||
crate trait TypeckQueryContext: crate::query::BaseQueryContext {
|
||||
query_prototype!(
|
||||
/// Find the fields of a struct.
|
||||
fn fields() for Fields
|
||||
);
|
||||
|
||||
query_prototype!(
|
||||
/// Find the type of something.
|
||||
fn ty() for Ty
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Here, the trait basically says: "the final context must be provide
|
||||
implementations of these two queries (`Fields` and `Ty`)". The macro
|
||||
specifies a method name which is expected to be the "camel case"
|
||||
version of the full query name (`fields`, `ty` respectively).
|
||||
|
||||
The `BaseQueryContext` trait is just the .. well .. basic query
|
||||
context operations. In general, I would expect to see some other
|
||||
modules instead, so something like:
|
||||
|
||||
```rust
|
||||
crate trait TypeckQueryContext: HirQueryContext { .. }
|
||||
```
|
||||
|
||||
where the `HirQueryContext` is a trait that defines the queries
|
||||
related to HIR construction.
|
||||
|
||||
Note that these traits are not limited to containing queries: we can
|
||||
basically add whatever methods we want that the "central context" must
|
||||
provide (e.g., I expect to add a global interner in there).
|
||||
|
||||
### Defining query implementations
|
||||
|
||||
In addition to defining the trait with the queries that it exports,
|
||||
the typeck module will also implement those queries. This is done most
|
||||
easily by using the `query_definition!` macro. Here is an example
|
||||
defining `Fields`:
|
||||
|
||||
```rust
|
||||
query_definition! {
|
||||
/// Test documentation.
|
||||
crate Fields(_query: &impl TypeckQueryContext, _def_id: DefId) -> Arc<Vec<DefId>> {
|
||||
Arc::new(vec![])
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is obviously a dummy implementation, but you get the idea.
|
||||
|
||||
### Defining query implementations the long-hand way
|
||||
|
||||
A query is really defined by a kind of "dummy" type that implements
|
||||
the `Query` trait. This is the sort of code that macro above
|
||||
generates:
|
||||
|
||||
```rust
|
||||
#[derive(Default, Debug)]
|
||||
crate struct Ty;
|
||||
|
||||
impl<QC> Query<QC> for Ty
|
||||
where
|
||||
QC: TypeckQueryContext,
|
||||
{
|
||||
type Key = DefId;
|
||||
type Value = Arc<Vec<DefId>>;
|
||||
type Storage = crate::query::storage::MemoizedStorage<QC, Self>;
|
||||
|
||||
fn execute(query: &QC, key: DefId) -> Arc<Vec<DefId>> {
|
||||
query.ty().of(key)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Customizing query storage etc
|
||||
|
||||
Each query defines its storage. Right now I only sketched out one form
|
||||
of storage -- memoized storage -- but eventualy I would expect to
|
||||
permit a few options here. e.g., "immediate" queries, that just
|
||||
*always* execute on demand, as well as queries that interact with an
|
||||
incremental system.
|
||||
## Example
|
||||
|
||||
There is a working `hello_world` example which is probably the best documentation.
|
||||
More to come when I expand out a few more patterns.
|
||||
|
|
Loading…
Reference in a new issue