metta.atoms
Source: extensions/python/metta/atoms.py.
Expose MeTTa atoms, the S/V/G factories, parsing, and matching.
The entries below reproduce the source signatures and docstrings.
ground
def ground(value: Any) -> Grounded:Carry a Python value whole, whatever it is.
This is the FFI boxing form. Structural wire conversion lives in :mod:
metta.wire;ground([1, 2, 3])therefore carries one list by identity instead of turning it into an expression.
fresh
def fresh() -> Variable:Mint a variable for a library-authored pattern without name capture.
arrow
def arrow(*positions: Any) -> Expression:Build an arrow type as data, mapping Python types through annotations.
typed
def typed(subject: Any, type_: Any) -> Expression:Build
(: subject type)as data; annotations are accepted as types.
if_
def if_(condition: Any, consequent: Any, alternative: Any = _OMITTED) -> Expression:Build either engine
ifarity; Pythoniflowers inside define.
not_
def not_(value: Any) -> Expression:Build a quoted or stored
notterm.
and_
def and_(*values: Any) -> Expression:Left-fold 2+ values through
and; retain its arity-0/1 partials.
or_
def or_(*values: Any) -> Expression:Left-fold 2+ values through
or; retain its arity-0/1 partials.
in_
def in_(member: Any, container: Any) -> Expression:Build a quoted or stored
interm.
seg
def seg(variable: Any) -> Expression:Build the named segment
(:seg $x), the variable's fifth face.A segment variable stands for a RUN of expression children rather than for one term, so
space[(S.Order, seg(V.rest))]matches an Order of any length and each answer'srestcolumn is the run it took. Bare...is the anonymous spelling and every occurrence of it is its own variable; a named segment can repeat, and the second occurrence then has to take the same run.The fence is the law's own theorem, not caution: general sequence unification is infinitary (Kutsia, Journal of Symbolic Computation 42(3), 2007), so a pattern outside the three proved-finite fragments refuses and names the rule.
parse
def parse(source: str) -> Atom:Read one form of MeTTa source into an atom, evaluating nothing.
Use
metta.forms()for a whole source program.parse()deliberately refuses empty input and multiple top-level forms rather than selecting one silently.Backed by the engine's own reader, with one improvement over sread/2: the variable names the DCG collects are kept, so parse("(Parent $x Bob)") contains Variable('x') rather than a machine name, and the same pattern built with V.x compares equal.
Crossed through apply() rather than once(). metta_py_parse/2 already has the functional shape, one ground input and one output, and every call that passes source text to eval(), run() or match() parses first, so this is a second crossing on top of the evaluation's own.
order_key
def order_key(atom: Atom) -> tuple:A sort key for atoms, in Prolog's standard order of terms.
sorted(atoms, key=order_key)Atom.lt delegates to this key, so explicit and plain sorting agree. The language's list-shaped expressions compare child by child; length is reached only when one expression is a prefix of the other. Variables come before numbers, strings, opaque objects, the empty expression, symbols, and nonempty expressions .
Two atoms that compare equal here are not necessarily the same atom: a key orders,
same_atomdecides identity.
substitute
def substitute(atom: Any, bindings: Mapping[str, Atom]) -> Atom:The atom with every bound VARIABLE replaced, keyed by variable name.
The internal primitive, for callers that hold names rather than atoms: a result row's columns, and
_match's directional bindings. An unbound variable stays itself, so a partial substitution is a narrower pattern rather than an error.
Atom.subsis the public method and the one implementation. It is keyed by ATOM because a bare name cannot say which kind it means on a surface that has both: this function reads{"x": ...}as the VARIABLE $x while abind()scope at the evaluation methods reads the same mapping as the SYMBOL x. Two meanings for one spelling is fine inside the library, where each caller knows which it holds, and is not something to publish.
unify
def unify(left: Any, right: Any, *more: Any) -> Mapping[Atom, Atom] | None:Unify atoms symmetrically, returning bindings or
None.Variables in any operand bind. Variadic means SIMULTANEOUS: every operand must agree under one substitution, folded through one shared binding store, so three rule heads unify at once the way two always did, and the pairwise call is the 2-ary case of the same signature. The returned substitution is normalized, so a chain such as
x = y, y = areports both names bound toa. Anonymous_occurrences remain fresh and bind nothing. This host matcher retains its historical no-occurs-check behavior; four-argument conditional unification is the engine form exposed bymetta.unify.Keyed by the VARIABLES themselves, which is what
Atom.subsaccepts, so a substitution this produces is one the library can apply. A bare name cannot say whether it means a variable or a symbol, and this language has both:bind({"x": 5})at the evaluation methods means the SYMBOL x.