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

ContractWebAssembly / JavaScriptNative Python
RuntimeA module instance in browser or NodeAn installed native SDK and CFFI
Inputstring, bigint or safe integer number, booleanstr, int, bool
Integer outputAlways bigintPython int
Domain scopeModule-local registryProcess-wide native registry
SessionOne owned policy/session/input and one live result per wrapperExplicit Engine, Ruleset, Edb, prepared Session and Result handles
Ground queryquery(predicate, terms)contains_fact(predicate, terms)
Resolved IDB enumerationenumerate(predicate, arity)enumerate_predicate_facts(predicate, arity)
ExplanationexplainTrue / explainFalseexplain_true / explain_false
FailureDatalogError with status and diagnostic snapshotMaelysDatalogError with status and diagnostic fields
LifetimeExplicit 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 declarationPython shortcutC builder macro
Predicate(name, arity, PRED_EDB)Predicate.edb(name, arity)MAELYS_DATALOG_EDB(name, arity)
`Predicate(name, arity, PRED_EDBPRED_QUERY)`Predicate.edb_query(name, arity)
Predicate(name, arity, PRED_IDB)Predicate.idb(name, arity)MAELYS_DATALOG_IDB(name, arity)
`Predicate(name, arity, PRED_IDBPRED_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_FACTPRED_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.