Expand description
Rust entry point for egglog.
use egglog::prelude::*;Most workflows are best expressed as an egglog program parsed and
run via crate::EGraph::parse_and_run_program: declare your
sorts, functions, and rules once in egglog text, then call into
the database from Rust around it.
use egglog::prelude::*;
let mut eg = EGraph::default();
eg.parse_and_run_program(
None,
"(datatype Math (Num i64) (Add Math Math))
(Add (Num 1) (Num 2))",
)?;Three cases need a Rust escape from the language:
- Driving the e-graph database directly — building rows, looking them up, iterating tables, running ad-hoc queries from Rust between (or instead of) running egglog rules.
- Custom rules whose right-hand side runs Rust — a
rust_rule/rust_rule_fullcallback fires on every match and gets a state handle to read/write the database. Useful when the rule body needs arithmetic, control flow, or data conversion that’s awkward in egglog itself. - Custom primitives callable from egglog expressions — new functions (e.g. arithmetic on a custom Rust type, an FFI call, a cost computation) that you want to invoke from egglog code as if they were built-ins. See “Extending egglog” below.
Cases 1 and 2 share the same surface: the crate::Read and
crate::Write capability traits, implemented on the
crate::PureState / crate::ReadState / crate::WriteState
/ crate::FullState wrappers. Inside a rule callback you
receive one of those wrappers directly; outside a rule you get a
crate::FullState from crate::EGraph::update:
use egglog::prelude::*;
let mut eg = EGraph::default();
eg.parse_and_run_program(None, "(function f (i64) i64 :no-merge)")?;
// Stage writes; flush once when the closure returns.
eg.update(|mut fs| fs.set("f", (1_i64,), 42_i64))?;
// Read in a separate `update` call — writes inside a closure
// aren't visible to reads in the same closure.
let v = eg.update(|fs| fs.lookup("f", 1_i64))?;
assert_eq!(v.map(|v| eg.value_to_base::<i64>(v)), Some(42));§Adding and reading facts
Methods on crate::Read and crate::Write, available via the
state wrappers:
crate::Write::set— write a function-table cell(set (f k) v).crate::Write::add— mint or look up a constructor / relation eclass.crate::Write::remove— remove a row from any subtype.crate::Write::union— union two eclassValues in the union-find.crate::Read::lookup— read a function’s output value.crate::Read::eclass_of— read a constructor’s eclass without minting.crate::Read::contains— row presence on any subtype.
§Iterating and querying
crate::Read::function_entriesvisits each entry of a function table via a callback, exposinginputs/outputas rawValues.crate::Read::constructor_enodesvisits each enode of a constructor / relation table, exposingchildren/eclass.crate::EGraph::queryruns a one-shot pattern query and returns oneHashMap<String, Value>per match, keyed by variable name. Useful for extracting bindings without compiling a persistent rule.
§Extracting terms
Pulling a Rust-side term out of an eclass:
crate::EGraph::extract_value— picks the lowest-cost representative under the default tree-additive cost model and returns it as acrate::TermIdin acrate::TermDag.crate::EGraph::extract_best/crate::EGraph::extract_variants— extract batches of roots under the default cost model.crate::EGraph::extract_best_with_cost_model/crate::EGraph::extract_variants_with_cost_model— do the same under a user-supplied cost model.
See the crate::extract module for the full API
(crate::extract::TreeExtractor, variant extraction,
sort-restricted extraction, custom cost types).
To get the (sort, Value) pair an extract_value call needs in
the first place, the easiest path is to let-bind a global name in
egglog and then resolve it with crate::EGraph::eval_expr:
use egglog::prelude::*;
let mut eg = EGraph::default();
eg.parse_and_run_program(
None,
"(datatype Math (Num i64) (Add Math Math))
(let $root (Add (Num 1) (Num 2)))",
)?;
let (sort, value) = eg.eval_expr(&exprs::var("$root"))?;
let (_dag, _term, _cost) = eg.extract_value(&sort, value)?;§Rules
rule— add a rule whose RHS is egglog code.rust_rule— add a rule whose RHS is a Rust closureFn(WriteState, &[Value]) -> Option<()>. Seminaive-safe.rust_rule_full— same but the closure receives acrate::FullState(can read the database in the callback). Runs:naivesince the body sees live state.run_ruleset/add_rulesetstep rules forward.
§Extending egglog
- Custom sort types:
BaseSort/ContainerSort. - Custom primitives: implement
crate::Primitiveplus one ofcrate::PurePrim/crate::ReadPrim/crate::WritePrim/crate::FullPrim(pick by what the body needs to do — pure, read-only, write-only, full). Register via the matchingEGraph::add_*_primitive. The state wrapper the body sees enforces what the body can do; see issue #772 for the seminaive-safety reasoning. - Simple pure primitives: the
add_primitive!/add_primitive_with_validator!/add_literal_prim!macros cover the “pure native function” case without writing out the full trait impl.
§Caveat: type unsafety at the column level
Eclass identifiers flow through the API as bare Values. A
Value returned from a Math constructor and a Value returned
from a List constructor are indistinguishable at compile time
and at runtime — callers track sort identity themselves.
crate::Write::union does not check that its arguments are the
same eq-sort; crate::Write::set and crate::Write::add do
not check that their column values match the table’s declared
column sorts. Arity (column count) and subtype (function vs.
constructor) are checked at runtime via crate::ApiError.
Re-exports§
pub use egglog::ast::Action;pub use egglog::ast::Fact;pub use egglog::ast::Facts;pub use egglog::sort::BigIntSort;pub use egglog::sort::BigRatSort;pub use egglog::sort::BoolSort;pub use egglog::sort::F64Sort;pub use egglog::sort::I64Sort;pub use egglog::sort::StringSort;pub use egglog::sort::UnitSort;pub use egglog::CommandMacro;pub use egglog::CommandMacroRegistry;pub use egglog::Core;pub use egglog::FullState;pub use egglog::PureState;pub use egglog::Read;pub use egglog::ReadState;pub use egglog::Write;pub use egglog::WriteState;pub use egglog::EGraph;pub use egglog::sort;pub use egglog::sort;
Modules§
Macros§
Structs§
Enums§
Traits§
- Base
Sort - A “default” implementation of
Sortfor simple types which just want to put some data in the e-graph. If you implement this trait, do not implementSortorContainerSort. Useadd_base_sortto register base sorts with theEGraph. SeeSortfor documentation of the methods. Do not overrideto_arcsort`. - Container
Sort - A “default” implementation of
Sortfor types which just want to store a pure data structure in the e-graph. If you implement this trait, do not implementSortorBaseSort. Useadd_container_sortto register container sorts with theEGraph. SeeSortfor documentation of the methods. Do not overrideto_arcsort. - Literal
Convertible - Trait for types that can be converted to/from Literal for use in validated primitives. This enables automatic validator generation for literal primitives.
Functions§
- add_
base_ sort - Add a
BaseSortto the e-graph - add_
constructor - Declare a new constructor table.
- add_
container_ sort - add_
function - Declare a new function table.
- add_
relation - Declare a new relation table.
- add_
ruleset - Create a new ruleset.
- add_
sort - Declare a new sort.
- rule
- Add a rule to the e-graph whose right-hand side is made up of actions.
- run_
ruleset - Run one iteration of a ruleset.
- rust_
rule - Add a rule to the e-graph whose right-hand side is a Rust callback.
- rust_
rule_ full - Like
rust_rule, but the action callback receives aFullState— it can read tables (ctx.lookup) in addition to writing them. Action callbacks ofFullPrimare only valid inContext::Full, so this helper marks the generated rule:naive: the body matches against the entire database every iteration instead of using the seminaive delta. Use this when the action genuinely needs to look up rows; preferrust_rulewhen the data can be bound via the matcher in the rule body.