Hornet: An embedded DSL for Logic Programming in Python.
Project description
Hornet
Horn clauses via Expression Trees — a Prolog-like embedded DSL for Python ≥ 3.13.
Hornet lets you write logic programs directly in Python. Instead of parsing Prolog strings, it hijacks Python's operator overloading and __call__ syntax to build expression trees, which a resolution engine then solves via unification and backtracking.
Installation
pip install hornet-dsl
Requires Python 3.13+. Dependencies: toolz, immutables.
Core Concepts
Terms
Hornet's term algebra mirrors Prolog's:
| Hornet | Prolog equivalent |
|---|---|
Variable('X') / symbols.X |
X (logic variable) |
Atom('foo') / symbols.foo |
foo (atom) |
symbols.foo(X, Y) |
foo(X, Y) (compound term) |
[1, 2, 3] / promote([1,2,3]) |
[1,2,3] (list) |
[H | T] via BitOr |
[H|T] (cons cell) |
Import symbols dynamically from hornet.symbols. Names starting with uppercase become Variables; lowercase become Atoms; _ is the anonymous wildcard.
from hornet.symbols import X, Y, parent, mortal, human
Facts and Rules
Facts and rules are built with .when():
db.tell(
parent('socrates', 'sophroniscus'), # fact
human('socrates'), # fact
mortal(X).when(human(X)), # rule: mortal(X) :- human(X).
)
Queries
from hornet import database
from hornet.symbols import X, mortal
db = database()
db.tell(human('socrates'))
db.tell(mortal(X).when(human(X)))
for subst in db.ask(mortal(X)):
print(subst[X]) # → socrates
db.ask() returns an iterable of substitutions. Each substitution maps variables to their bound values.
Built-in Predicates
Hornet ships a standard library of predicates pre-loaded in every database:
| Predicate | Description |
|---|---|
equal(X, Y) |
Unification (X = Y) |
unequal(X, Y) |
Negation of unification |
let(R, Expr) |
Arithmetic evaluation (R is Expr) |
arithmetic_equal(X, Y) |
Arithmetic equality |
smaller(A, B) / greater(A, B) |
Numeric comparison |
call(G) |
Call a goal term |
once(G) |
Call G, commit to first solution |
findall(O, G, L) |
Collect all solutions |
member(X, L) |
List membership |
append(A, B, C) |
List concatenation |
reverse(L, R) |
List reversal |
select(X, L, R) |
Select element from list |
length(L, N) |
List length |
maplist(G, L) |
Apply goal to each list element |
is_var, nonvar, is_atom, is_int, … |
Type checks |
write, writeln, nl |
I/O |
cut |
Prolog cut (!) |
fail |
Always fails |
true |
Always succeeds |
repeat |
Succeeds infinitely |
throw(E) |
Raise an exception |
ifelse(T, Y, N) |
Soft-cut conditional |
phrase(G, L) |
DCG query |
Arithmetic
Arithmetic expressions are built using Python's operators on symbolic terms and evaluated lazily by let:
from hornet.symbols import X, Y, R, let, arithmetic_equal
# R is X * Y + 1
db.ask(let(R, X * Y + 1))
# supported: + - * / // % ** ~ & | ^ << >>
Definite Clause Grammars (DCGs)
Hornet supports DCG notation via DCG() and DCGs():
from hornet import DCGs, database
from hornet.symbols import S, NP, VP, noun, verb, det, phrase
db = database()
db.tell(*DCGs(
S.when(NP, VP),
NP.when(det, noun),
VP.when(verb),
det.when(['the']),
noun.when(['cat']),
verb.when(['sleeps']),
))
for subst in db.ask(phrase(S, ['the', 'cat', 'sleeps'])):
print('parsed!')
DCG rules are automatically expanded to difference lists. The inline(goal) escape hatch lets you embed regular Prolog goals inside a DCG body.
Extending with Python
Register native Python predicates using the @predicate decorator:
from hornet import database, predicate, unit
from hornet.clauses import Database, Subst
from hornet.combinators import Step
from hornet.symbols import X
db = database()
@db.tell
@predicate(X)
def _(db: Database, subst: Subst) -> Step[Database]:
val = subst[X]
print(f'native hook: {val}')
return unit(db, subst.map)
Architecture
Hornet is built on three layered abstractions:
Tail-call elimination (hornet.tailcalls): A trampoline/thunk system prevents RecursionError on deep recursion. Functions decorated with @tailcall return deferred frames; trampoline() drives them iteratively.
State monad (hornet.states): Used internally during clause compilation to thread fresh-variable environments without mutation. Exposed via with_state, get_state, set_state.
Triple-barrelled continuation monad (hornet.combinators): The resolution engine carries three continuations — success (emit a substitution), failure (backtrack), and prune (implement cut). Goals are functions (ctx, subst) → Step, and the combinators then, choice, prunable, neg, if_then_else compose them.
Examples
The examples/ directory includes:
append.py— list splitting via backtrackingqueens.py— N-queens constraint solverfizzbuzz.py— FizzBuzz via DCGssymdiff.py— symbolic differentiation and simplificationparsing.py— natural language parsing with a German grammarturing.py— a Turing machine interpreterhanoi.py— Towers of Hanoi with Turtle graphics
License
MIT. See LICENSE.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hornet_dsl-0.3.1a1.tar.gz.
File metadata
- Download URL: hornet_dsl-0.3.1a1.tar.gz
- Upload date:
- Size: 28.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: pdm/2.26.6 CPython/3.12.11 Linux/6.18.12-329.current
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7de0e1d60bd2b4b5382de3106b88d3c138410a34fdd38284791d962ce351181b
|
|
| MD5 |
74672aab86befa0ef1013da16fdab5ec
|
|
| BLAKE2b-256 |
5e4582bd60b3cae3be71295e3c44c6aaa9d7a0c9950f29527df79edd22bc096b
|
File details
Details for the file hornet_dsl-0.3.1a1-py3-none-any.whl.
File metadata
- Download URL: hornet_dsl-0.3.1a1-py3-none-any.whl
- Upload date:
- Size: 21.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: pdm/2.26.6 CPython/3.12.11 Linux/6.18.12-329.current
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e89ebfa852cf243600e8593785bd53f64de547a6aee22647639d22fe85c124ba
|
|
| MD5 |
a8d0151e526cc919008ce0076429d5ef
|
|
| BLAKE2b-256 |
32407379c24ab540027d31f0836ad016716c3c50d880b622e6def758313ef15b
|