Bindings
Bindings overview
Choose between the native C, Python, and WebAssembly integration surfaces.The Python and JavaScript bindings use the same public C SDK and reference solver. Their host-language interfaces differ; neither exposes native solver structures as application objects. Both accept a declaration before loading a policy, preserve typed values and return native explanation documents.
In 0.10.0, there is one Python package, maelys_datalog: the former
Python-next implementation became the current Python binding.
The old C-shim binding is no longer shipped; its
Python V1 documentation remains under Deprecated.
Choosing a binding
| Contract | WebAssembly / JavaScript | Native Python |
|---|---|---|
| Runtime | A module instance in browser or Node | An installed native SDK and CFFI |
| Input | string, bigint or safe integer number, boolean | str, int, bool |
| Integer output | Always bigint | Python int |
| Domain scope | Module-local registry | Process-wide native registry |
| Session | One owned policy/session/input and one live result per wrapper | Explicit Engine, Ruleset, Edb, prepared Session and Result handles |
| Ground query | query(predicate, terms) | contains_fact(predicate, terms) |
| Resolved IDB enumeration | enumerate(predicate, arity) | enumerate_predicate_facts(predicate, arity) |
| Explanation | explainTrue / explainFalse | explain_true / explain_false |
| Failure | DatalogError with status and diagnostic snapshot | MaelysDatalogError with status and diagnostic fields |
| Lifetime | Explicit close() | Explicit close() or context managers |
The JavaScript API resolves and copies all enumerated symbols. Python also has an explicitly raw result view; such IDs belong to the live result and cannot be reused as input or across results. Neither binding silently interns an unknown string during a query.
Predicate declarations across Python and C
The Python shortcuts and optional C builder macros express the same origin-plus-query roles. Python creates immutable Predicate values; the C macros initialize public predicate declarations. Neither form registers a domain by itself. For the full vocabulary rules, see Python registries and C registries.
| General Python declaration | Python shortcut | C builder macro |
|---|---|---|
Predicate(name, arity, PRED_EDB) | Predicate.edb(name, arity) | MAELYS_DATALOG_EDB(name, arity) |
| `Predicate(name, arity, PRED_EDB | PRED_QUERY)` | Predicate.edb_query(name, arity) |
Predicate(name, arity, PRED_IDB) | Predicate.idb(name, arity) | MAELYS_DATALOG_IDB(name, arity) |
| `Predicate(name, arity, PRED_IDB | PRED_QUERY)` | Predicate.idb_query(name, arity) |
Predicate(name, arity, PRED_POLICY_FACT) | Predicate.policy_fact(name, arity) | MAELYS_DATALOG_POLICY_FACT(name, arity) |
| `Predicate(name, arity, PRED_POLICY_FACT | PRED_QUERY)` | Predicate.policy_fact_query(name, arity) |
Session model
A new MaelysPlayground.create() creates its own module and native state. A
wrapper holds at most one result; release it before reusing its input/session.
Python offers prepared sessions with the same one-result lease, as well as
convenience solves that create separate sessions for independent results.
Each Python Engine and its handles remain on their creating thread; workers
create their own Engine.
A failed solve publishes no partial result. Bounded input append and native resolution are separate admissions: an input may fit the public buffer yet exceed a solver limit when materialized.
Shared semantics
- Predicate origins and QUERY permission come from explicit domain declarations.
- Quoted constants written in policy source need declared atoms; runtime input strings do not. Policy facts also need POLICY_FACT permission.
- Ground queries can find authorized EDB, policy and derived facts. Enumeration returns derived IDB facts only.
- Missing symbols produce false queries but NOT_FOUND explanations. A known fact without an IDB witness produces a not-derived Why-true document.
- Truncated Why-false is an incomplete search, not a complete proof.
- SMALL/LARGE capacities and aggregate arithmetic limits remain engine limits. Host-language integers do not extend the policy language.
Memory and packaging
Neither binding is zero-malloc: JS/Python objects, conversions and text buffers allocate. Prepared engine workspaces have their own explicit storage contract. Wasm memory growth does not make an engine profile elastic.
Consume one coherent SDK version. JavaScript wrapper, declaration, glue and Wasm travel together; Python builds against one installed SDK prefix. The 0.10.0 migration removes the historical native-object interfaces and aliases.
See the TypeScript / JavaScript reference for the Wasm surface and the Python reference for the native binding. The Python V1 reference describes the deprecated pre-0.10.0 interface and is retained solely for migration.