diff options
author | tcmal <tcmal> | 2023-05-24 21:08:57 +0000 |
---|---|---|
committer | Aria <me@aria.rip> | 2023-10-01 17:31:29 +0100 |
commit | 2c1525849f3e47587f58c7afbd3020c8076c9f4e (patch) | |
tree | 0e0ef3cbb76692cf3c73ed599ddf0a3af6bfd0e3 /incria/src | |
parent | 689851f773994685e74e9bf20263a7732a00b982 (diff) |
more docs
Diffstat (limited to 'incria/src')
-rw-r--r-- | incria/src/lib.rs | 59 |
1 files changed, 59 insertions, 0 deletions
diff --git a/incria/src/lib.rs b/incria/src/lib.rs index c8c35d6..a9ebda0 100644 --- a/incria/src/lib.rs +++ b/incria/src/lib.rs @@ -4,6 +4,65 @@ //! //! This is similar to the [query system used by the Rust compiler](https://rustc-dev-guide.rust-lang.org/query.html), but implemented using async. //! By seperating the tracking of dependencies from the calculation logic, you can write clean code that re-runs only when it needs to, and can be parallelised with little extra work. +//! +//! # Concepts +//! +//! ## Mappings +//! +//! A mapping associates a key with a value. This can include: +//! * Functions +//! * Reading from a file +//! * Data from another part of the program, such as a UI +//! +//! A [mapper](`self::Mapper`) is responsible for exposing this mapping, and for caching and invalidating the mapping as appropriate. +//! +//! ## Dependencies +//! +//! For mappings that use other mappings to get a value (most commonly functions), we need some way of knowing what mappings they depend on, and when those mappings have changed. +//! +//! To do this, we use a directed acyclic graph (DAG). +//! Each mapping is represented by a node on this graph. +//! An edge from one node to the other means that the result of the first node's mapping depends on the result of the second node's mapping, and should be recomputed if the second node may have changed. +//! +//! If we only have functions as mappings, then we will construct this graph and probably never use it. +//! In the more likely case that we have some 'impure' parts, such as reading a file, then we will eventually need to say that a mapping's value may have changed. We do this by marking the node as 'dirty'. +//! +//! When a node is marked dirty, any node that depends on it directly or indirectly is also marked dirty. +//! The next time the mapping corresponding to a dirty node is requested, it should be recomputed and not cached. +//! +//! # Usage +//! +//! ## Functions +//! +//! To memoise computations, you can normally use a [`ThunkMapper`](`self::thunk::ThunkMapper`). +//! This memoises a given 'thunk', which is simply a function with one input, one output, and that is pure except for its use of other mappers. +//! +//! ```rust +/*! +# use incria::{thunk::{Thunk, ThunkMapper}, Mapper}; +# use std::{future::Future, pin::Pin}; +struct ExampleThunk; +impl Thunk<usize, usize> for ExampleThunk { + fn compute(&self, key: usize) -> Pin<Box<dyn Future<Output = usize> + Send + '_>> { + Box::pin(async move { + key + }) + } +} + +type ExampleMapper = ThunkMapper<usize, usize, ExampleThunk>; +# async fn example() { +assert_eq!(*ExampleMapper::new(ExampleThunk).get(&1).await, 1); +# } +*/ +//! ``` +//! +//! # Writing Mappers +//! +//! When writing mappers, you will need to interface with the [dependency API](`self::deps`) directly. +//! See the module-level documentation for details on which functions to call and when. +//! +//! Normally, you will keep track of dependency IDs alongside some other information, then expose some poll function externally that will mark all the required nodes dirty. pub mod deps; mod mapping; |