ekzhang / crepe

Datalog compiler embedded in Rust as a procedural macro
Apache License 2.0
460 stars 16 forks source link

Iterative usage #12

Closed gzsombor closed 3 years ago

gzsombor commented 3 years ago

It would be nice, if run wouldn't consume the crepe instance - or there were an incremental 'step()' function, which just returns the newly created relations - so if there is need to call external processes or even async functions, it can be implemented. I would imagine something like this:

 crepe! {
        @output
        pub struct Sum(u32, u32, u32);

        struct Input(u32);

        Sum(a + b, a, b) <- Input(a), Input(b), a < b;
    }

fn test_incremental() {
    let instance = Crepe::new();
    instance.extend(&[Input(2), Input(3)]);
    let (result, ) = instance.step();
    assert_eq(result, [Sum(5, 2, 3)]);
    instance.extend(&[Input(7)]);
    let (newResult, ) = instance.step();
    assert_eq(result, [Sum(9, 2, 7), Sum(10, 3, 7)]);
    let all = instance.all_sum();
    assert_eq(result, [Sum(5, 2, 3), Sum(9, 2, 7), Sum(10, 3, 7)]);
 }

What do you think?

ekzhang commented 3 years ago

It's a good idea. The semi-naive evaluation method lends itself well to this kind of incremental evaluation. Unfortunately, I don't think this is compatible with stratified negation.

If you're looking for a more flexible/streaming system, maybe check out differential dataflow?

gzsombor commented 3 years ago

Thanks for the suggestion. Initially, I saw differential dataflow, but it looked more complicated, and I liked your approach of declarative rules, but I guess, this is needed for this case. (If under stratified negation you meant a way to retract previously created relations)

ekzhang commented 3 years ago

Yes, stratified negation means rules that have a "does not exist" clause in them. For a trivial example,

use crepe::crepe;

crepe! {
    @input
    struct Number(i32);

    struct Double(i32);

    @output
    struct Out(i32);

    Double(2 * x) <- Number(x);
    Out(x) <- Number(x), !Double(x);
}

This doesn't fit in the iterative model because relations don't monotonically grow; if you were to start with Number(2), then your output would be [Out(2)]. However, if you then extend the input with Number(1), then Out(2) would no longer exist due to the negation, and your output would be [Out(1)].

Negation is logically valid when stratified (i.e., no negative cycles between relations), and Crepe supports it. However, this stratification isn't compatible with incremental evaluation. That's why I think it's kind of tough to add this API.

ekzhang commented 3 years ago

Closing this now because the suggested design doesn't work. Note that you can call external functions within Crepe rules, though it doesn't work with async. See this Reddit comment for an example.