The "missing" graph datatype already exists. It was invented in the '70s
tylerhou.com
tylerhou.com
The hunt for the missing data type - https://news.ycombinator.com/item?id=39592444
I've used https://s-arash.github.io/ascent/ in a handful of Rust sideprojects, and it was very nice: you can treat it as a built-in graph datatype in the same way Hillel and the OP talk about, because it's a proc macro that can reuse all the rest of your Rust program datatypes and functions with your Datalog queries, instead of an entirely separate program you have to bolt on like if you want to integrate SWI-Prolog or something.
OTOH, my experience with these systems has been that they are popular with enterprise ISOs, but have very high inaccuracy (lots of false positives, and lots of misses on the kind of vulns they purport to detect), and while they are marginally useful, they seem do more for security checklist compliance than for security.
Ultimately other forms of static analysis won't guarantee my code is good, but I still use linters.
Similarly, tests (unit, integration, etc..) won't prove that nothing can go wrong - but I'm not going to stop writing them.
I'd love to be using a "perfect" language which could prove my program correct at compile time, and would presumedly also make static analysis borderline magical for these use cases - but until that's an option I think there's a place for all these tools. (Beyond simply ticking compliance boxes)
With all that said, false negatives are indeed a hard problem - and one not helped by large orgs having painful bureaucracy around false _positives_.
Some of this appears to be the fault of tooling (need better filtration, deferral, weighting) but much of it seems a side effect of institutional silo's rather than a lack of perfect analyses.
TLDR; pobody's nerfect, but more info generally better than less
It's used in various policy evaluators (unsurprisingly) around access management, but also for gatekeeper [2] which allows security teams to define constraints around kubernetes resources, similarly conftest [3] can do the same for terraform
For a lot of simple cases it's a fair bit more complicated (and unfamiliar) than tailored query languages, but really shines for matching over a complex graph of interlinking resources and then evaluating Boolean logic against the matches.
[1]: https://www.openpolicyagent.org/docs/latest/policy-language/....
[2]: https://open-policy-agent.github.io/gatekeeper/website/
It might be interesting to discuss to what extent did "graph databases" (you know who you are!) get a foothold because relational database platforms were slow to develop convenient notations and algebras (libraries) for working with abstract graphs. As Hou points out, there is some justifiable skepticism about the argument that graph databases are somehow intrinsically "more efficient" than relational databases for working with graphs. This would be surprising, given the obsession with optimization and performance that dominated the database community for many years, while issues like usability were a bit neglected (leaving the door open for other communities to innovate graph databases and data visualization platforms.) (Another point, don't I sometimes need both relational and graph algebras?)
Because I'm looking mainly for expressive convenience (with good in-memory runtime performance) it's not enough to know that Datalog can represent any abstract graph. If I find textbook pseudocode for, say, maximum matching in graphs, or transitive closure or connected components, how hard will it be to program in the target graph programming system? I'm confident that Datalog, Recursive SQL, and Cymbal or Gremlin can all get the job done, but at what expressive cost (assuming my algorithm is not already a graph language primitive)? Will anyone still even recognize the algorithm.
Maybe I'm missing what you're saying here, but matrices are not "equivalent" to edge lists, they are abstract mathematical objects decoupled from whatever storage method you use in a computer. For example, SuiteSparse:GraphBLAS has four storage formats: dense, bitmap, sparse, and hypersparse. None of these are equivalent to edge lists.
Edge lists are a way to store a representation to a graph, but they are not a graph. Like a matrix, a graph is a abstract mathematical concept that has different methods of representing itself in computers. But mathematically, graphs and matrices are isomorphic, every graph is a matrix, and every matrix is a graph. The isomorphism extends to operations as well, every BFS is a matrix multiplication, and vice versa.
Can you expand on that?
A cool result of this is, for example, taking an adjacency matrix and squaring it is the "Friend of a Friend" graph. It takes every node/row and multiplies it by itself, returning a matrix that are adjacent to the adjacencies of each node, ie, the friends (adjacencies of the adjacencies) of friends (adjacencies) of the nodes.
Deeper traversal are just higher powers, a matrix cubed are the friends of the friends of the friends. You literally say `A @ A @ A` in Python, and you're done, no dictionary or lists or loops or anything else.
A picture is worth a thousand words, see figure 7 of this paper:
https://arxiv.org/pdf/1606.05790.pdf
Also check out figure 8, this shows how incidence matrices can work to represent hyper and multi graphs. An pair of incidence matrices reprsent two graphs, one from nodes to edges and the other from edges to nodes, these are n by m and m by n. When you multiply them, you get a square adjacency matrix that "projects" the incidence into an adjacency. This can be used to collapse hypergraphs into simple graphs that use different semirings to combine the multiple edges.
For some pretty pictures of this kind of stuff, check out CoinBLAS (note I am not a crypto-bro, it was just a very handy extremely large multi-graph that I could easily download in chunks to play with):
The GP's claim that graphs (not matrices) are equivalent to edge lists is almost correct. The textbook definition of graphs is G = (V, E) , where V are the set of vertices and E are the set of edges. Implicitly E contains relationships between some of the vertices, so the only thing in the V which isn't part of an edge in E are vertices that are not related to any edge. So if you have a connected graph, you can unambiguously define it by way of a set of edges (~= "edge lists") without explicitly mentioning the vertices.
In short, I don't see any fundamental error in GP's quoted sentence unless you're trying to be extra pedantic.
This says article basically says "use an edge list" and plug it into a very fancy library/database that may internally transform the representation and/or otherwise do magic to evaluate the graph better.
And I mean like sure, but now you're just sort of burying the problem. You're saying "I've invented the one true graph library and that library will handle all the hard parts."
But datalog has limitations, stuff as simple as weighted (much less graphs with non-integer annotations which require some declarative analysis) are the realm of academic research.
Like, when "we don't support page rank" (https://link.springer.com/article/10.1007/s11280-021-00960-w) is noted in the research paper from 2022, I think saying "datalog" solves all these problems seems incorrect.
> However, despite the high performance and declarativeness benefits of Cog, it does not support common complex data analytics, such as PageRank... We present Nexus, a Datalog evaluation system that overcomes all the aforementioned challenges.
And Figure 21 shows two Datalog systems that seem to be able to run the PageRank algorithm: https://link.springer.com/article/10.1007/s11280-021-00960-w...
> But datalog has limitations, stuff as simple as weighted [edges]... are the realm of academic research.
Weighted edges are well-supported by Soufflé, which is stable and I would be comfortable using it in production. Soufflé also supports ADTs, so it also can augment paths with proofs in the same manner as Egglog. I used a more "research" implementation (Egglog) for the post because they have an online interactive demo. It is true that there is academic research being done on Soufflé, but there is academic research being done on e.g. Rust, and people still use Rust in production.
I also explicitly say that there needs to be more research into better Datalog engines and integrating Datalog support into programming languages. ("Languages could have amazing graph support! In maybe a decade? And only after lots of research effort?")
So it's not that the graph datatype already exists, it's that, just like the referenced article posits, there is no good representation. And rather than lament and gnash teeth, we use a neato programming / query language to turn that into a strength.
In reality the original (hillelwayne) article is saying that the "graph" datatype is missing, as a standard type, which is true. Imperative language implementations abstract that away to libraries, which may be graph databases (datalog being one of many), or may be more tightly coupled things like networkx for cases where you need some kind closer knit integration and custom computation.
Like, taking a step back, you're saying that no single representation is a panacea. And the original article is taking the same stance, that no single graph representation or library is a panacea, because so much is computation dependent.
> until some necessary thing isn't supported
Taking a third side, I dont think the possibility of a missing algorithm should remove the positives of not assuming a graph format apriori.
What I mean is, it seems that the approach of "use a data representation and implement your algorithm using something like queries" is meant to be an argument for empowering graph library writers to support a wider array of inputs (e.g., all of them) without specifying different implementations for those inputs.
That's cool. Maybe I misunderstood what you said.
I think this gets back to the thing I initially brought up, which is that if you take this as the guidance, features as simple as weighted edges jumps to the realm of SoTA. It's perhaps good research guidance, but that's not useful for me who needs to analyze a graph today.
This is correct. What is shown is in fact a programming environment with query engine/optimizer using an internal DSL. That is cool, and that is something you see with Sql, Tensor, etc but that is a full-blown thing.
Not a datatype.
If that's true - Datalog is based on relational algebra - then I'd be very interested to see the author's reference because the way I know it Datalog is a subset of Prolog, without functions so that termination can be guaranteed, and without negation-as-failure so that it's monotonic, depending on the variant, and in any case, incomplete (because termination).
For example, see:
What you always wanted to know about Datalog (and never dared to ask)
https://ieeexplore.ieee.org/document/43410
Which begins with:
Datalog, a database query language based on the logic programming paradigm, is described.
So the right abstraction to think about Datalog is logic programming, and the First Order Predicate Calculus, not the relational calculus. It's true that Datalog is used as a database query language, unlike Prolog that is a general purpose language, but that's because of the incompleteness of Datalog, which is something you probably want for a db query language, but certainly don't want for a general purpose programming language.
Seriously. Learn Prolog. It's a powerful language and you'll never worry about the Object-Relational Impedance-Mismatch ever again in your life. The only reason not to learn it is that you will forever be sad that you can't use it in your day job. Or you'll find one where you can, like I did.
Dude, I wasn't going to be mean at you. I was going to say I get it, learning Prolog is hard and you need to understand the subject matter very deeply, in a whole other level than the usual languages we learn at uni, Python, or java, or, dunno Ada in the olden days.
Prolog's not easy to learn. But it pays off in spades for the effort.
Datalog /happens/ to be a subset of Prolog syntactically, but their semantics are very different. An analogy is how LL parsers and LR parsers can both parse context free grammars, but their properties are different -- LL parsers parse from "top down" and thus will not halt on left-recursive grammars (just like Prolog) while LR parsers parse from "bottom up" and can support left-recursive grammars, at the cost of increased space.
Note that the demand transformation / magic set optimization, which is a common optimization, closes the gap between Datalog and Prolog semantically. In particular, it gives Datalog the best of both worlds: (1) increased speed, because it is not computing all possible facts, just the ones "reachable" from the query (like Prolog) and also (2) termination guarantee because all programs in the base Datalog language terminate.
>> Datalog /happens/ to be a subset of Prolog syntactically, but their semantics are very different. An analogy is how LL parsers and LR parsers can both parse context free grammars, but their properties are different -- LL parsers parse from "top down" and thus will not halt on left-recursive grammars (just like Prolog) while LR parsers parse from "bottom up" and can support left-recursive grammars, at the cost of increased space.
You have to be careful when you're talking about "semantics" because there's a difference between the semantics of a language and the semantics of its execution. Like you say, Datalog is normally evaluated bottom-up, with what we call a TP Operator. If a Datalog program is evaluated top-down, like a Prolog program, then it's not guaranteed to terminate. On the flip side, Prolog is normally evaluated by SLD-Resolution, implemented as a Depth-First Search with backtracking and so it can get stuck in loops on left-recursions, as you say very correctly, but it can also be evaluated by SLG-Resolution, implemented as Breadth-First Search with memoization (a.k.a. tabling) in which case it _doesn't_ get stuck in loops on left-recursions.
In short, no I wouldn't agree that Datalog "happens" to be a subset of Prolog syntactically. That's what it is, by design. Evaluation is a different matter.
And this is where we get into the discussion of trade-offs.
>> Note that the demand transformation / magic set optimization, which is a common optimization, closes the gap between Datalog and Prolog semantically. In particular, it gives Datalog the best of both worlds: (1) increased speed, because it is not computing all possible facts, just the ones "reachable" from the query (like Prolog) and also (2) termination guarantee because all programs in the base Datalog language terminate.
Well I'm not sure whether that's right because I'm not sure what are the two "worlds" we get the best of. Efficiency is one thing. When you say that Datalog is not computing all facts, I understand this as saying it doesn't have to ground the Herbrand base of a logic program, like ASP has to, for example. True.
But the important trade-off (in my opinion anyway) is between efficiency and completeness, which is another way to look at termination guarantees, a.k.a. decidability. If a language, under some inference rule, is decidable, then it is not complete. And that's the limitation with Datalog, which comes from the fact it's a function-free language [1]. Without functions there is much you can't do. For example, function free Datalogs can't have lists, the main data structure in Prolog (other than well, "terms") because the Prolog list-constructor operator ([Head|Tail]) is a function. Without functions you can't do integer arithmetic. And so on.
I'm not sure how Datalog systems deal with those limitations (I don't really work with Datalog). I suspect they bolt-on some extra-logical system to do e.g. arithmetic, pretty much like Prolog does. In any case, the limitations of Datalog are I guess the reason it's popular as a database query language, because you don't really need arithmetic, or lists, in a database query language. But recursion, that terminates, is nice to have.
Btw, if you have a reference to the creation of Datalog older than the one I linked to, please share it. I've been trying to find the "original" datalog reference for a while and couldn't. I have no idea at this point who, exactly, came up with it, and how.
___________________
[1] Prolog is not function free, but calls its functions "terms". Which is very confusing because it also calls everything else a "term"; including constants, which it calls "atoms", and literals, i.e. atoms and negations of atoms. If you're lost, that's because you should. Prolog is a terminological atrocity.
1982 was as far as I got last time I went digging: https://news.ycombinator.com/item?id=34819400
Umm… I hate to be contrary, but FOPC is a logic about relations. You might think that it is about objects, and it is, but only indirectly, mediated by relations. Constants which refer to objects only appear in the argument lists of relations.
FOPC is all about relations. It’s the language we use when we want to formally define a relation.
On a side note, is there a Bitter Lesson for datatypes, the way there is for algorithms?
I will look into form pointer chasing. Any good reads that you would recommend for software devs who are interested in electrical engineering or rather understanding how to build hardware?
The real power of using matrices for graphs is that you can use Linear Algebra to process them. Instead of "edge and node" thinking, LA brings the power of matrix multiplication and semirings to graph algorithms. Instead of working about edgelists, hashmaps of visited nodes, thread pools and when to fork or not to fork, by using a standard like the GraphBLAS you can just express an algorithm as a system of matrix operations, and the underlying library can choose how to run it, and on what hardware.
For example, the current state of the art GraphBLAS implementation is SuiteSparse:GraphBLAS, has a JIT compiler that runs graph algorithms on a variety of CPUs and CUDA GPUs. The same sparse deep neural network inference code that is a few lines of Python can run on a chromebook all the way up to a large GPU system with no changes, the only difference is the size of the graph and the time it takes to process it.
As graphs get into billions and trillions of edges, writing algorithms by hand that target different architectures gets extremely difficult and tedious. Future versions of SuiteSparse have a lot of exciting feature planned, including operation fusion and distributed processing. Retargeting hand written algorithms will be a thing of the past.
One of the best parts about the GraphBLAS is that the graph really does have a "type" in the programming language sense, it's a Matrix, and the same operators and operations you expect to work are there. There is great support for both Julia and Python at the moment for beginners and data science oriented folks to dive in quickly.
Here's an interesting paper on how to express centrality algorithms like PageRank and Triange Centrality (disclaimer: I am one of the paper authors):
https://www.researchgate.net/publication/356707900_The_Graph...
I also created an introductory video some time ago explaining the very basic concepts:
The article responds to another, noting that there's inherent tradeoffs.
ex. "with 100 nodes and 200 edges...If we use an adjacency matrix representation...we need a 100×100 matrix containing 200 ones and 9,800 zeros. If we instead use an edge list we need only 200 pairs of nodes."
(n.b. this flattens a lot of the interesting info in both articles into 'ah, matrix!" - open to that being true but it feels unlikely)
The GraphBLAS is a sparse matrix library, it does not store the non-present values.
Also, a non-present value may or may not be zero. For example in shortest path algorithms, the non present value is positive infinity.
When I think of graphs, I usually think of ones used for representing the alignment of biological sequences. Nodes have two sides, left and right, and both sides have their own neighbors. A forward traversal >v of node v enters from the left, reads the sequence stored in the node, and exits from the right. A reverse traversal <v enters from the right, reads the reverse complement of the sequence, and exits from the left.
Sometimes the graphs are path-centric. There is a fixed set of paths (or walks, if you prefer), and the graph is induced by them. The typical query is path extension: if you have already traversed >A<B>C>D, what are the possible left/right extensions according to the underlying paths matching the context. In a good graph representation, you can do this by maintaining a small state that does not grow significantly with the length of the context or the number of underlying paths.
Matrices don't feel like a good abstraction for graphs like this.
There are a number of representations for graphs like this, mostly differing by whether they are mutable or immutable, faster or more space-efficient, and graph-centric or path-centric. Generic algorithms use either node identifiers, which are consistent across graph representations, or opaque handles, which are representation-specific and often more efficient to use. Sometimes you select the representation according to the algorithm you want to use. Sometimes you select the algorithm according to the graph you already have (because conversions can be expensive). And sometimes you adjust the problem definition to reach something that can be computed efficiently with the available tools.
George Box was very specifically talking about statistical models when he coined that aphorism. Matrices are linear algebra and graphs are graph theory, I find it hard to think they are not correct and useful models.
> A forward traversal >v of node v enters from the left, reads the sequence stored in the node, and exits from the right. A reverse traversal <v enters from the right, reads the reverse complement of the sequence, and exits from the left.
I'm not an expert in this field but I'm guessing you're talking about De Bruijn graphs, which can be very elegantly modeled with incidence matrices, here's an example of one using the GraphBLAS that downloads data from BioPython, loads it into incidence matrices and graphs it. This is just a simple example, SuiteSparse can handle many billions of edges:
https://github.com/Graphegon/Graphony?tab=readme-ov-file#exa...
Traversing bidirectionally is quite easy, the upper triangle of a matrix are the directed outgoing edges, and the lower triangle are the incoming. This style of "push/pull" optimization is common in the GraphBLAS.
> In a good graph representation, you can do this by maintaining a small state that does not grow significantly with the length of the context or the number of underlying paths.
Again if I understand you correctly, in the GraphBLAS this is accomplished by using accumulators and masks. During traversal data can be accumulated, with a stock operator or one you define, into a vector or matrix, and that object can be used to efficiently mask subsequent computations to avoid unnecessary work or determine when you've reached a termination condition.
> Matrices don't feel like a good abstraction for graphs like this.
Mathematically, graphs and matrices are isomorphic. Regardless of algorithm or storage format like edge lists, tuples or CSR, every graph is a matrix, and vice versa. And if you have a matrix, you have linear algebra to operate on it.
Some people don't like Linear Algebra to operate on graphs, so I guess for them it is "not good", but on the other hand, it's Linear Algebra and Graph Theory, whose roots date back to the 2nd century BC, forward through great minds like Descartes and Euler, permeating every kind of math, science, physics and engineering discipline humans have ever created. That's a strong argument for its goodness.
Now it is entirely possible, likely even, that the current SuiteSparse implementation doesn't have exactly the tool needed or maybe not the precise best storage format, but these missing pieces do not invalidate the underlying mathematical foundation that it's based on.
In that model, nodes have separate sets of left edges and right edges, and the edges connect node sides rather than nodes. For example, there can be an edge between the right sides of two nodes. The edges are undirected, but you can't exit a node from the side you entered it. Alternatively, the edges become directed once you fix the orientation of the node visit. Then the successors of a node in one orientation are its predecessors in the other orientation. Some graph representations have an underlying directed graph with separate nodes for the two orientations, but that's an implementation detail people usually don't want to think about.
The path-centric model can be thought as predicting the token preceding/following a context. Node D may have right edges to >E, <F, and >G, but if you are in context <B>C>D, only >E and <F are available. And if you extend the context to the left to >A<B>C>D, then your only option may be <F. This is a primitive operation that may be repeated a million times per CPU-second in a graph with hundreds of millions of nodes. You often don't know which extensions you are going to take until you have processed the sequences in the previous ones.
Matrices are a useful model when you want to do similar things to most nodes in a graph. But when you are exploring the graph locally in an iterative fashion, it's more convenient to think about nodes and edges.
So it's not that the graph datatype already exists, it's that, just like the referenced article posits, there is no good representation. And rather than lament and gnash teeth, we use a neato programming / query language to turn that into a strength.
Man is spirit. But what is spirit? Spirit is the self. But what is the self? The self is a relation which relates itself to its own self, or it is that in the relation [which accounts for it] that the relation relates itself to its own self; the self is not the relation but [consists in the fact] that the relation relates itself to its own self.
Would this specification suffice for a sentient datalog program? Or is datalog itself sentient?
You've captured my intrigue, and now I want to explore datalog, so thank you for writing the article.
reachable(V, V) :- vertex(V).
reachable(From, To) :-
arc_from_to(From, Next),
reachable(Next, To).
One can show that Datalog with two very conservative and simple extensions (allowing negation of extensional database relations, and assuming a total order on the domain elements) captures the complexity class P, so can be used to decide exactly those properties of databases (and hence graphs) that are evaluable in polynomial time, a major result from descriptive complexity theory.An example of such a property is CONNECTIVITY ("Is the graph connected?"), which can be easily expressed with Datalog on ordered databases, where we assume 3 built-in predicates (such as first/1, succ/2 and last/1) to express an ordering of domain elements:
connected(X) :- first(X).
connected(Y) :- connected(X), succ(X, Y), reachable(X, Y).
connected :- last(X), connected(X).
If such an ordering is not available via built-in predicates, then we can easily define it ourselves for any given concrete database by adding suitable facts. Also negated EDB relations can be easily defined for any database as concrete additional relations.If you view each rule as a query, however, looping over rules does capture Datalog semantics. Furthermore, by optimizing over rules using the relational algebra, one can derive algorithms "equivalent" to traditional graph algorithms.
(I don't think you would disagree with me; just want to clarify for other people who might be reading.)
By the same logic, the SQL datatype is also missing.
If you enjoy this kind of thinking, we recently released GFQL for dataframe-native accelerated graph querying & compute that build on some of the under-the-hood insights here
Imagine Neo4j Cypher, except no need for a database -- just import it -- and automatically vectorizes for significantly faster CPU+GPU performance. This is fundamentally similar to the kinds of optimized engines the article's datalog approach enables. In fact, one of our big internal questions was whether to use ~datalog syntax as the frontend!
We've run it on 100M+ edge graphs in seconds on some of the cheapest GPUs you can get, and are getting ready for the next rev with aggregate compute as it's becoming more important for our community: https://github.com/graphistry/pygraphistry/blob/master/demos...
Here are the researchers, in no particular order:
Max Willsey (Berkeley): https://www.mwillsey.com/ and his PL class (https://inst.eecs.berkeley.edu/~cs294-260/sp24/)
Joe Hellerstein (Berkeley): https://dsf.berkeley.edu/jmh/
Dan Suciu (UW): https://homes.cs.washington.edu/~suciu/ and his DB theory class (https://berkeley-cs294-248.github.io/)
Remy Wang (UCLA): https://remy.wang/
Hung Ngo (relationalAI): https://hung-q-ngo.github.io/
The Hydro Project at Berkeley: https://hydro.run/
Pick a classic algorithm, say triangle counting, implement it in Datalog, compare against GBBS [1] and come back here to report results.
The Prolog-derived syntax is routinely extended because the core is typically too simplistic/inexpressive to be directly useful, e.g. see https://www.fdi.ucm.es/profesor/fernan/des/html/manual/manua...
I expect this is going to show up as a really popular model at some point - don't have to have two separate worlds for queries and other logic.
That's definitely the dream. Another point along that spectrum (from the author of Apache Calcite): https://github.com/hydromatic/morel
As a nuts-and-bolts developer, I like <https://www.cozodb.org> for datalog-style data operations. It interoperates nicely with python, C, and Swift. It comes with some basic graph algorithms, data loaders, etc. I haven't found any bugs, but fair warning: it's not 1.0, and the developers seem to be pivoting to AI vector data support.
Data modeling is a lot closer to NoSQL key/value stores, or FoundationDB. The append-mainly model requires maintenance compaction. I typically tokenize before putting anything into the database and never had scaling problems with large but not internet-scale uses. Explain-query is a bit opaque.
But mostly I’m impressed someone read an article, thought “not quite true” and created a well written piece like this, in what? A day and a half?
There's interesting depth here in that constraint satisfaction problems can be modeled as finding homomorphisms (I associated this line of thought with Moshe Vardi and others). Big patterns into small targets tend to look like coloring problems or SAT, whereas small patterns into big targets look like queries. Isomorphism is right in the middle.
tringle(X,Y,Z) :- edge(X,Y), edge(Y,Z), edge(Z,X).
square(X,Y,Z,W) :- edge(X,Y), edge(Y,Z), edge(Z,W), edge(W,X).Granted, datalog is simpler, has a strong theoretical foundation, is more secure and securable, has over 50 years of technological development, is currently wicked fast...and its opportunities for and- and or-parallelism, combined with its single-assignment variables, means that its perfect for the multicore, shared-cache chips we'll have to build to keep Moores Law going. I could go on...
....but that's all been true since the Clinton Administration. Heck, most of it was true since the Nixon administration, but NOBODY WANTS TO USE IT.
I can go to my boss and persuade him to let me implement something in python rather than c++. But he wouldn't even understand what I was proposing if I argued for datalog over sql. All he wants to do is finish updating Jira so that he can go home. I can't launch into a week-long tutorial about what a Horn clause is, or why negation-by-failure is more coherent than the corner-cases which SQL's three-valued logic has...
We're stuck with SQL. Fortunately, the OP's great points about using relations to implement graphs are still valid even w/o the datalog.
Sucks but honestly, how could I blame him for thinking that way? He--like practically every other boss on the planet-- so utterly lacks any capacity to understand what datalog brings to the table, that really, on what basis could he even make the call?
And you can't even blame him for not taking two weeks off to investigate it--new gee-whiz techniques and methodologies appear every day, most of which promise more than they can possibly deliver. If he spent all his time studying them, he'd get nothing else done.
So why should this be any different? Besides, everybody else on the planet manages to get their shit done with SQL, so just shut up and get with the program.
Uh...I'm not bitter tho...chuckle
And again, it's hard to blame the boss, he's just as scared about it as everybody else. Why should he take a chance and rock the boat?
Especially for something like datalog. No iteration, just recursion? What's with this bizarre syntax? Why are the variables write-once??? Not everybody is just going to be able to take all that in and see how all these weird little pieces add up to a superior solution.
body :- head should be
head ⊢ body