For anyone getting caught up in the notation, note that the actual example expressions are immaterial; you might as well replace the strange looking relational algebra operators with "="s or "+"s. At it's core the article is covering different ways to represent graphs, and demonstrating that one particular method allows concise representation of graphs that aren't easily represented by linear expressions (linear in the graph sense, not the linear algebra sense).<p>In order, the representations are:<p>1. "Inlining", a direct tree structure, in C++-like syntax something like `struct Node {vector<Node>;}`. The insight here is that if you're not storing a tree, some of the nodes need to be duplicated.<p>2. "Let Bindings", An indirect tree structure, like `struct Node {vector<Node*>;}`. Here, the insight is that instead of storing nodes, you store <i>names of</i> nodes (in this case pointers), so that the nodes can be re-used. Note that the nodes themselves may be stored somewhere else in the same expressions.<p>3. "Self-Reference", same as above, but a node can store the name of itself, or one of its ancestors.<p>4. "Fixpoint Operator", Instead of explicitly storing a graph, we store a transformation from one graph to another, then say the graph we want equals that transformation repeatedly applied to some starting graph, until the transformation's output equals its input (a.k.a. a fixed point is found). Something like `struct ImplicitGraph {Graph initial; Graph (*)(Graph) transform;}`. If you're unfamiliar with C++ function pointers, transform is a function from Graph to Graph. The "hole" the author talks about refers to the fact that the output graph of the transformation can contain copies of the input graph, so the "hole" is the transform parameter; there's a "hole" in the output graph until the parameter is known. Since the transformation can be arbitrarily complex, any graph can be represented, although typically, you'd want as simple a transform as possible.<p>5. "Explicit Listing of Edges", this method is actually more straightforward than the previous, but it requires thinking about the graph a little differently. It's exactly what it says on the tin; instead of the graph being implicit in the structure of the data, the edges (a.k.a. links or lines) are explicitly listed. There's more than one way to do this, for example a straightforward way might look like `struct Graph {set<Node>; set<pair<Node*, Node*>>;}`. From the example in the article, it looks like Datalog is closer to something like `struct Graph {set<Node> initial_nodes; set<optional<Node> (*)(Node)> generators;}`. Kind of like in 4., you start with a set of nodes, then repeatedly apply each generator to each node until you stop getting new nodes. The edges in this case are between each node and the nodes it generates.<p>Finally, all of these types of representations also exist syntactically, not just structurally. After all, an expression in a given syntax is just a way of representing some structure, the same way a bunch of bytes in memory can be a way of representing some structure. And each of these representations has strengths syntactically and structurally.