///|
pub struct Applier {
  apply : (EGraph, Map[String, Id]) -> Id raise
}

///|
pub fn Applier::apply(
  self : Applier,
  egraph : EGraph,
  subst : Map[String, Id],
) -> Id raise {
  (self.apply)(egraph, subst)
}

///|
pub fn Applier::pattern(rhs : Pattern) -> Applier {
  Applier::{ apply: (egraph, subst) => build_rhs(rhs, egraph, subst) }
}

///|
pub fn Applier::from_fn(f : (EGraph, Map[String, Id]) -> Id raise) -> Applier {
  Applier::{ apply: f }
}

///|
pub fn build_rhs(
  pat : Pattern,
  egraph : EGraph,
  subst : Map[String, Id],
) -> Id raise {
  match pat {
    Pattern::Var(name) | Pattern::VarIf(name, _) =>
      match subst.get(name) {
        Some(id) => egraph.find(id)
        None => fail("missing variable in substitution")
      }
    Pattern::Sym(name) => egraph.add(make_symbol(name))
    Pattern::Num(n) => egraph.add(make_number(n))
    Pattern::Wild => fail("wildcard not allowed on RHS")
    Pattern::Node(op, children) => {
      let child_ids : Array[Id] = Array::new()
      for child_pat in children {
        child_ids.push(build_rhs(child_pat, egraph, subst.copy()))
      }
      egraph.add(make_enode(op, child_ids))
    }
  }
}