5个版本

0.2.0 2023年3月31日
0.1.3 2023年3月31日
0.1.2 2023年3月31日
0.1.1 2023年3月31日
0.1.0 2023年3月31日

#853数据库接口

MIT 许可证

52KB
1.5K SLoC

verde

一款清爽的增量计算引擎。

什么是增量计算?

增量计算处理一个看似简单的问题

给定一个纯函数 f 和一些输入 x,计算 f(x),并 记忆化 输出。下次,如果用相同的输入执行 f,则返回记忆化的输出而不是重新计算。然而,如果输入发生变化,则重新计算值,并重复。

术语

verde 的核心组件是一个 数据库。数据库存储所有记忆化的数据(跟踪类型),以及跟踪每个记忆化函数的状态(一个 查询)。此外,数据库还允许对值进行内部化处理,以便它们是去重的,并且可以通过简单的 ID 检查进行比较(内部化类型)。最后,数据库还允许查询安全的 '旁路',以便在不成为查询输出的一部分的情况下收集诊断信息(可推送类型)。

入门

首先,我们必须定义 verde 将要跟踪的结构体。可以这样完成

#[derive(verde::Tracked, Eq, PartialEq)]
struct MyTrackedStruct {
    #[id]
    id: UniqueIdentifier,
    value: u32,
}

标记为 #[id] 的字段用作每个跟踪类型的唯一标识符。它必须在每个查询中是唯一的(一个查询函数在给定不同输入时不得生成具有相同 ID 的结构体,但不同的查询可以)。注意,需要实现 Eq 特性才能实现 Tracked,并且 ID 必须实现 CloneEqHash

接下来,我们必须定义任何可推送的类型。

#[derive(verde::Pushable)]
struct MyPushableStruct {
    value: u32,
}

实现 Pushable 不需要其他特性。

内部化作为一个便利功能提供

#[derive(verde::Interned, Eq, PartialEq, Hash)]
struct MyInternedStruct {
    value: u32,
}

需要实现 CloneEqHash

最后,我们必须定义查询函数。

#[verde::query]
fn double(ctx: &verde::Ctx, input: verde::Id<MyTrackedStruct>) -> MyTrackedStruct {
    let interned = ctx.add(MyInternedStruct { value: 5 });
    assert_eq!(ctx.geti(interned).value, 5);
    ctx.push(MyPushableStruct { value: 5 });
    let input = ctx.get(input);
    MyTrackedStruct {
        id: input.id,
        value: input.value * 2,
    }
}

查询是普通函数,必须将 &Ctx 作为第一个参数。它们可以像普通函数一样简单调用,verde 将透明地处理所有增量逻辑。

然而,在我们可以调用我们的查询之前,我们必须首先创建我们的数据库

#[verde::storage]
struct Storage(MyTrackedStruct, MyInternedStruct, MyPushableStruct, double);

#[verde::db]
struct Database(Storage);

使用 storage 属性来创建一个 存储结构体,该结构体包含数据库将知道的数据类型。将几个存储结构体组合在一起形成一个数据库。这种多层次的方法允许模块化设计,其中每个crate都可以定义一个存储结构体,而驱动crate只需包含它们。

最后,我们可以运行我们的查询

fn main() {
    let mut db = Database::default();
    let db = &mut db as &mut dyn verde::Db;
    let input = db.set(MyTrackedStruct { id: UniqueIdentifier::new(), value: 5 });
    let output = db.execute(|ctx| double(ctx, input));
    assert_eq!(db.get(output).value, 10);
    let mut pushables = db.get_all::<MyPushableStruct>();
    assert_eq!(pushables.next().map(|x| x.value), Some(5));
    assert_eq!(pushables.next(), None);
}

依赖项

~1.3–7MB
~44K SLoC