scallop-lang/scallop: one language, one pluggable algebra, and a default build that skips both bindings
Framework and Language for Neurosymbolic Programming.
At a glance
- What is it?
- A DataLog-derived language whose defining feature is a generalized provenance semiring: the same logic program runs as discrete inference, as probabilistic inference, or as differentiable reasoning inside a PyTorch module, with the algebra chosen at runtime on the command line or at construction time in the Python binding. The workspace's default build excludes that Python binding and the WebAssembly target, and the flagship differentiability example stops mid-statement.
- Who is it for?
- This suits a research group that wants to put symbolic reasoning inside a learned model and needs to know which algebra a given rule is being interpreted under, which this makes explicit rather than hidden. The cost is a demanding setup: a nightly Rust toolchain as the default, an end-of-life interpreter for the Python path, and three languages in one repository.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 99 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 3, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One language, one pluggable algebra
The interesting part of this project is not DataLog syntax, which is unremarkable, but the layer underneath it. The language is built on a generalized provenance semiring framework, and arbitrary semirings can be configured. That single mechanism is what lets the same program perform discrete logical reasoning, probabilistic reasoning, or differentiable reasoning, because the algebra determines how values combine and what a fact's weight means. It also makes this a different kind of DataLog solver rather than just another one: the reasoning mode is a parameter, not a different language. The example program illustrates the point with a taxonomy where an animal's identity is recognised from an image with explicit weights, and a count aggregate derived through a rule. Nothing about that program changes if you want certainty instead of weights, or gradients instead of either.
The interpreter refuses probabilities unless you name the algebra
The semantics are selected at the point of execution rather than written into the program. The command-line interpreter interprets a file with a particular extension, and the documentation is explicit that probabilistic input is not accepted by default. To obtain the resulting probabilities you pass a flag naming a provenance semiring, and the example given names a simple semiring described as sufficient for probabilistic reasoning. The consequence is worth understanding rather than memorising: a program that contains weighted facts will not quietly produce weighted answers, it will be refused, which is a much safer default than silently treating a weight as a constant. The Python binding makes the same choice at construction instead of at invocation, defaulting to what the example calls unit provenance. So the same program run two ways will interpret its weights differently unless you are deliberate about it.
The default build excludes the Python binding and the WebAssembly target
The workspace manifest is a virtual one at the repository root, with no package section of its own, which means version numbers live in the individual crates rather than in one place. It lists ten active members: the core, a code generator, the two compiled front ends, the interpreter, the REPL, the Python binding, a WebAssembly target, and three small support crates. Six more are present but commented out, five of them under a heading for laboratory work and one a Node binding. What matters more is the separate list of default members, which is a strict subset: it omits the Python binding and the WebAssembly build. So a plain build in the root directory produces the core and the three command-line tools and nothing else, and anyone wanting the Python module or the browser playground has to name those targets.
The flagship differentiability example stops mid-attribute
The integration the project exists to demonstrate is a PyTorch module that wires a digit recognition network into a logical sum relation, and the readme gives the class definition for it. What the class shows is genuinely interesting: the context is constructed with a provenance semiring whose name refers to differentiable top-k proofs and a parameter for how many proofs to keep, and the network's ten outputs are mapped into two logical relations as input facts through an input mapping that assigns one fact per output index. The logical rule then sums two of those relations. What the example does not show is the end. The last line assigns the reasoning module from an attribute access that is cut off mid-word, so the one thing a reader would most want to see, how the logical module is called forward inside the network, is missing. Two smaller things accompany it: the same word is spelled three different ways across the page, and a comment in one code block uses a REPL prompt prefix while the surrounding blocks do not.
Nightly Rust by default, and an end-of-life interpreter for the bindings
The prerequisite section asks you to install Rust with the nightly channel set as the default, and gives a rustup command that does exactly that:
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
$ rustup default nightlyFor a research language that is a real adoption cost, because a nightly toolchain is one that upstream can break between one day and the next, and a project whose build depends on it inherits that volatility. The Python path asks for a different version again: the virtual environment and the conda instructions both name an interpreter release that has been past end of life for over a year. So the two halves of the same project target a moving Rust toolchain and a fixed, retired Python interpreter. The Python extension is built with a dedicated Rust-to-wheel tool rather than plain pip, which is the right choice for a native module, and it means one more install step between cloning and importing.
Three binaries, three different install mechanisms
The repository produces a command-line interpreter, a compiler and a read-eval-print loop, and each is installed differently. The first two go through named targets in the project makefile. The REPL goes through a direct package install pointed at its own directory in the workspace. The Python binding goes through a build target that has to be run inside the virtual environment you created first, and the documentation is careful to repeat that requirement twice. The editor plugin adds a fourth path, requiring a Node packaging tool installed globally and then a make target that produces a packaged extension archive in a directory under the extra packages tree, which you then install through the editor's own interface. Four artefacts, four mechanisms, three languages. Nothing here is broken, but the first-run experience is four separate discoveries rather than one.
Two rule syntaxes and one restricted negation
The language offers rules in two surface forms, which is a deliberate ergonomic choice rather than an accident. Traditional DataLog uses the horn-clause separator with a comma-separated body. There is also a form closer to logic programming, using assignment with explicit connective keywords for conjunction and disjunction. The readme presents them side by side as alternatives for the same reachability rule, so a reader coming from either background finds something familiar. Facts come in the same two shapes, a single constant-argument fact or an inline set, and either can carry a weight prefix, which is how probabilistic facts are written. Probabilistic rules put the weight on the head. Negation is supported but scoped: it is stratified negation, demonstrated with a small odd and even example built from a bounded natural-number relation, so a reader should not assume arbitrary negation is available.
The newest tag dropped its prefix and the release line stopped
The release history has three entries and they are inconsistent in a way that will bite anyone scripting against them. The two older tags carry a version prefix in their names and so does the newer one, except that the newest does not, and the human-readable release titles disagree with each other on capitalisation as well. The dates are the larger story: the newest release is from August 2024 and the branch was last pushed in June 2026, so the published line has been frozen for the better part of two years while the default branch moved on. That gap is consistent with a project whose experiment crates sit commented out in the workspace manifest. The default branch is also still named the older convention, and two repository files follow that older style too, a makefile and a changelog in lower case, alongside a documentation directory singular where most projects use the plural.
Editorial conclusion
This suits a research group that wants to put symbolic reasoning inside a learned model and needs to know which algebra a given rule is being interpreted under, which this makes explicit rather than hidden. The cost is a demanding setup: a nightly Rust toolchain as the default, an end-of-life interpreter for the Python path, and three languages in one repository. Read which semiring your run actually selected before trusting a probability, because the interpreter refuses probabilistic input by default and the two bindings pick different defaults.
Frequently asked questions
What is the provenance semiring in Scallop?
It is the layer the language is built on, and it is configurable: arbitrary semirings can be set, which is what allows the same program to perform discrete logical reasoning, probabilistic reasoning, or differentiable reasoning. The command-line interpreter refuses probabilistic input by default and needs a flag naming a semiring to produce probabilities, while the Python binding selects it when constructing its context and defaults to unit provenance.
How do I use Scallop from Python with PyTorch?
Through a binding whose context object takes relations, facts and rules, runs the program, and lets you read results back. For the PyTorch case the documented example constructs the context with a differentiable top-k proofs provenance and a parameter for how many proofs to keep, maps the network's outputs into logical relations as input facts, and writes a rule summing them. The example's final line is truncated in the documentation.
What does building Scallop from source require?
Rust installed with the nightly channel set as the default, then a clone and named make targets for the interpreter and compiler. The read-eval-print loop installs through a direct package install pointed at its own workspace directory instead, and the Python binding needs a virtual environment or conda environment plus a separate Rust-to-wheel build tool, run from inside that environment.
Does a default cargo build include the Scallop Python and web targets?
No. The workspace manifest lists ten active members, but its default-members list is a smaller subset that omits the Python binding and the WebAssembly build. Six further members are commented out, five of them experimental crates under a laboratory heading, plus a Node binding. The root manifest is a virtual one with no package section, so versions live in the individual crates.
What rule syntaxes does Scallop support?
Two. Traditional DataLog rules with the horn-clause separator, and a logic-programming style using assignment with explicit conjunction and disjunction keywords, shown side by side for the same reachability rule. Facts come as a single constant-argument form or an inline set, and both can carry a weight prefix, which is how probabilistic facts are written. Negation is available but is stratified rather than arbitrary.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/scallop-lang-scallop)