Pluggable Backend interface + FlowLog backend as an experimental crate#1
Closed
oflatt-claude wants to merge 5 commits into
Closed
Pluggable Backend interface + FlowLog backend as an experimental crate#1oflatt-claude wants to merge 5 commits into
oflatt-claude wants to merge 5 commits into
Conversation
Introduce `egglog-backend-trait`, a small, documented SPI so downstream crates can implement their own e-graph execution backend: - `Backend`: object-safe trait for table lifecycle, rules, iteration, value registries, and primitives. Advanced features (action registry, containers, subsumption, `or`-patterns) are optional default-provided methods, so an external backend implements only the small required core. - `BackendExt`: blanket-impl ergonomic sugar (for_each, with_execution_state, base_value_constant, register_container_ty) over any `Backend`. - `RuleBuilderOps`: mirrors the bridge's RuleBuilder; backends accumulate into their own IR and finalize on `build()`. - `impl Backend for egglog_bridge::EGraph` (the in-memory reference backend) lives in the trait crate, leaving egglog-bridge untouched. Route the frontend `EGraph` through `Box<dyn Backend>` and add `EGraph::with_backend(..)` so a custom backend can be installed. Sort `column_ty`/`register_type` now take `&BaseValues` / `&mut dyn Backend`. Bridge-only paths (`unstable-fn` TableAction, scheduler match instantiation, file loading) downcast via the documented escape hatch. The full egglog test suite passes (747 file tests + unit/integration), with no new failures. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Switch experimental's egglog/egglog-ast/egglog-reports deps from the pinned
upstream git rev to the local `../egglog`, per the monorepo's intent, and
forward-port the API drift to the newer egglog:
- FunctionRow (removed) -> reconstruct full rows via a `for_each_full_row`
helper that tries `function_entries` and falls back to `constructor_enodes`.
- CostModel::enode_cost now takes `&Enode<'_>`; key is `enode.children`.
- lookup_function -> `read(|rs| rs.lookup(name, RawValues(..)))`.
- with_full_state -> `update(|state| { .. ; Ok(()) })`; row reinsertion via
`state.set` (functions) / `state.add` + `state.union` (constructors).
- GenericRule.naive: bool -> eval_mode: RuleEvalMode (+ include_subsumed).
`cargo build` is clean and all 86 experimental tests pass on the local egglog.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…al crate `egglog-experimental/flowlog/` is a worked example that a third party can implement their own egglog execution backend against the public `egglog-backend-trait` SPI — it depends only on that trait plus the neutral `egglog-core-relations`, not on the reference `egglog-bridge`. It ports the FlowLog backend (host-side rule iteration with the rule-body join running on an in-process differential-dataflow dataflow, `dd_native.rs`) onto the minimal `Backend` trait. Because the minimal trait's `MergeFn`/`DefaultVal` drop the proof-mode / native-merge / term-build / value-fold / native-UF / identity-on-miss variants, that machinery is stripped; merges collapse to the plain Relation/Old/New/Min modes. The Interpret-mode differential-dataflow join (the real path `.egg` files run on) is fully preserved — rule bodies still lower to DD `.join` chains on a shared timely worker with signed-delta epoch stepping, and an unlowerable rule shape still panics (no host nested-loop fallback). Its heavy edition-2021 deps (differential-dataflow, timely, flowlog-rs, vendored differential-dogs3) are isolated in the crate's own workspace so they don't leak into the egglog workspace. Tests: `smoke.rs` runs a datatype + insertion + `(run 1)` + `(print-size)` through `EGraph::with_backend(FlowlogEGraph)`; `rewrite_join.rs` confirms commutativity (single-atom join) and distributivity (multi-atom join) rewrites fire through the DD join and unify via congruence. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- egglog-backend-trait: drop redundant `|e| f(e)` closures in the bridge impl. - egglog: remove a now-useless `QueryEntry -> QueryEntry` `.into()` in the rule translator (lookup/call return QueryEntry directly through the trait). - rustfmt across the touched egglog and flowlog files. `cargo clippy --tests -- -D warnings` and `cargo fmt --check` pass on the egglog workspace; egglog / experimental / flowlog test suites are green (the only failing test, eqsat_basic_term_encoding_roundtrip, is pre-existing on the base). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… backend Add `flowlog/tests/corpus.rs`, which runs a curated subset of egglog's own `.egg` corpus on BOTH the reference in-memory backend and the FlowLog backend and asserts they agree — unconditionally (no env gating), as part of `cargo test`. Both egraphs are built identically (`EGraph::default()` vs `EGraph::with_backend(FlowlogEGraph)`), so the frontend drives the same operations into each backend. Every file gets `(print-size)` appended and the two backends' per-function row-count output is compared: if FlowLog's differential-dataflow engine derives a different final database than the reference, the counts diverge and the test fails. All listed files (bool/i64/bitwise/interval/before-proofs/merge-saturates/string) currently match the reference exactly. Files needing features outside FlowLog's subset (proofs, containers, subsumption, complex merges, non-DD-lowerable rules) are omitted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
oflatt-claude
force-pushed
the
backend-interface
branch
from
July 2, 2026 19:58
a9e6dd7 to
b234e03
Compare
Owner
Author
|
Superseded by saulshanabrook#4 (opened against the upstream repo). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a small, documented pluggable backend interface to egglog so a third party can implement their own e-graph execution engine, and demonstrates it with a FlowLog (differential-dataflow) backend living as a crate under
egglog-experimental/.Draft — opened for review. Base is
encoding-rebuild-via-or(ff04e0e) so the diff is exactly the 5 backend commits.Commits
egglog-backend-traitcrate; frontendEGraphholdsBox<dyn Backend>;EGraph::with_backend(..).egglog-experimental/flowlog/..eggcorpus through flowlog, unconditionally.Design
Backendtrait's required surface is ~20 methods (tables, rules, iteration viaScanEntry, value registries, primitives). Advanced features (action registry, containers, subsumption,or-patterns) are optional default-provided methods, so an external backend implements only the small core. Ergonomic generics live in aBackendExtblanket impl.impl Backend for egglog_bridge::EGraphitself (orphan-rule-legal), reusing the bridge's id/config types.egglog-backend-trait+egglog-core-relations(not the reference bridge), in its own isolated cargo workspace so its heavy differential-dataflow deps don't leak.FlowLog scope
The Interpret-mode differential-dataflow join (what
.eggfiles actually run on) is fully preserved — rule bodies lower to DD.joinchains on a shared timely worker with signed-delta epoch stepping, and an unlowerable rule shape still panics (no host nested-loop fallback). Because the minimal trait'sMergeFn/DefaultValdrop the proof-mode / native-merge / term-build / value-fold / native-UF / identity-on-miss variants, that machinery is stripped; merges collapse to the plain Relation/Old/New/Min modes.Tests
clippy --tests -D warnings✓,fmt --check✓, 747 file tests + all unit/integration pass.bool/i64/bitwise/interval/before-proofs/merge-saturates/string).Known pre-existing failure (in the base, not this PR)
eqsat_basic_term_encoding_roundtripfails on the base commitff04e0e("Term encoding: rebuild via correlated OR"), independent of this work. The correlated-orrebuild (rebuilding_rules_correlatedinproofs/proof_encoding.rs) emits per-branch throwaway variablesl{i}_that the desugarer re-prefixes with the internal-symbol marker on every pass, soresolve_programis not idempotent for them andtext_twice != text_thrice. Fixable by minting those vars with the internal prefix (or as wildcards) so they aren't re-freshened.Limitations
clone_boxedisunimplemented!on flowlog (push/pop unsupported, as in the original).🤖 Generated with Claude Code