This was a microblog post at first, but I'm crossposting it for visibility and because I'm not 100% convinced the microblog won't collapse at any moment.

# VSAs are an acyclic e-graph rooted at a single e-class

I've thought that VSAs were acyclic e-graphs for a while but I never actually checked the e-graph paper. Now I'm pretty convinced.

This gh thread/short paper discuss some relationship between e-graphs and VSAs, but don't show the actual definition https://github.com/egraphs-good/egg/discussions/104

```
Program := Value
| App { op: Function, children: List[Program] }
-- by FlashMeta etc
VSANode := Leaf { asts: Set[Program] }
| Join { op: Function, children: List[VSANode] }
| Union { children: Set[VSANode] }
-- invariant: all programs represented
by a VSANode are equivalent
-- Leaf: all the children asts must be equivalent
-- Join: inductively all the children are equal,
so it's by operator property
-- Union: also inductive
-- based on the egg paper
-- (https://dl.acm.org/doi/pdf/10.1145/3434304)
-- but I interpreted some stuff
-- because there's a lot of indirection
ENode := Final Program
| Join { op: Function, children: List[EClass] }
EClass := Set[ENode]
EGraph := Set[EClass]
```

So if you observe that each case of an `ENode`

can represent a (possibly singleton) set on its own, then you can collapse it into one definition and treat the whole thing as a set. Then, the main difference is that an `EGraph`

makes the distinction that a `Union`

represents an `EClass`

and lets an `EGraph`

be a set of e-classes. This definition makes a lot of sense when cycles are allowed, since there's not a certain root.

You could also reuse the `VSANode`

definition and let `EGraph := Set[VSANode]`

, assuming that your type system can't check that it's acyclic

The indirection i referred to in the egg paper is to do with e-class IDs and sticking stuff in an arena, but for VSAs I just used pointers. In an older version of my interactive-vsa site I actually use the `Rc`

address as the id of the html node