Skip to content

gaia lang: resolve the false compose/decompose duality; add generalize as a blessed composition pattern #759

Description

@kunyuan

Problem

compose and decompose read as a dual pair but are unrelated constructs living on different ontological layers. Worse, the actual missing counterpart of decompose — the ampliative upward move, generalize — is not expressible in current gaia lang at all.

1. The compose word family spans three layers with zero duality

lexeme layer actual semantics
decompose Claim (logic) declares whole ≡ formula(parts) — a constrained equivalence (parts↔atoms bijection, acyclic, once per claim)
composite legacy Strategy surface (dsl/strategies.py) composite strategy constructor, deprecated compat
compose / composition Action (template) decorator capturing an action DAG into a named+versioned Compose action (runtime/composition.py:153)

gaia lang has genuine dual pairs elsewhere (land/lor, forall/exists, implies/iff), so users — and the agents that author most packages — reasonably infer compose = inverse of decompose. The language actively teaches a wrong mental model. The CLI asymmetry (17 statement-emitting author verbs vs the file-based compose/composition registration verbs) is a downstream symptom.

decompose itself needs no structural inverse: whole ≡ formula(parts) is an equivalence, belief flows both ways under BP, and "build the whole from parts" is the same statement written from the other authoring perspective.

2. The real gap: generalize was flattened out by the operator redesign

The operator/strategy redesign (#412, #414) moved Strategy down into IR. The migration guidance for legacy induction (docs/foundations/gaia-lang/knowledge-and-reasoning.md:345) is:

declare each support step with derive / observe and let factor-graph topology accumulate evidence

This dropped a load-bearing speech act. Legacy induction() forced a composition warrant ("Are observations independent? Do they support the same law?"). With flat per-instance arrows, each action carries its own implication warrant, but the joint validity question — observation independence — has no syntactic home. Nothing forces an author to assert it; topology accumulates evidence regardless. That is exactly the double-counting failure mode we have already paid for in real packages (the Galileo package re-formalization).

IR still has the machinery: StrategyType.INDUCTION (ir/strategy.py:49), formalize.py templates, and the induction special-case in coarsen.py — but for new-style packages nothing produces it; it is a dead path kept alive for compat.

3. The key structural fact: induction is a composition

ir/strategy.py:49 already says it:

INDUCTION = "induction" # CompositeStrategy wrapping shared-conclusion abductions

Generalization is not an atomic inference form. It is a bundle of shared-conclusion evidential supports plus one joint warrant. The per-instance layer already exists in the language: infer(obs, hypothesis=law, given=[aux...], p_e_given_h=..., p_e_given_not_h=...) is precisely "law (+ auxiliary premises) derives this observation", with Bayes inverting the belief flow. Per-experiment auxiliary premises stay heterogeneous and live in each instance's given= (load-bearing, shares modus-tollens blame — Duhem–Quine) or background= (context only). A generalize verb must NOT absorb or restate any of that.

So generalize should be a blessed composition pattern, not a new primitive:

e1 = infer(obs_mars,    hypothesis=law, given=[aux_mars...],    p_e_given_h=...)
e2 = infer(obs_jupiter, hypothesis=law, given=[aux_jupiter...], p_e_given_h=...)

generalize(law, over=[e1, e2],
           reason="independent instruments and independent orbit fits; no shared systematics")

lowering to the existing Compose action / IR (runtime/action.py:216, ir/compose.py) — no new Action class, no ir-schema bump.

What the sugar adds over a bare @composition (and why bare composition is not enough):

  1. Mandatory independence warrant — bare warrants= is optional free text; the entire anti-double-counting value is that the question cannot be skipped.
  2. Shared-conclusion validation — every bundled action must support the same law.
  3. Recognizable identitycoarsen / review / render need to see "this is an inductive argument", not an anonymous DAG. Prefer a type tag on Compose over matching a magic name= string.
  4. Plain-call ergonomics — the @compose(name=, version=) decorator + pyproject registration ceremony is built for reusable templates, not a one-off induction inside a paper formalization.

Proposal

Two independent steps, strictly incremental:

Step 1 — naming (small). Deprecate the verb-form alias compose; make the noun composition the canonical name for the action-template mechanism (the alias composition = compose already exists at runtime/composition.py:200; CLI already has gaia author composition). This dissolves the false compose/decompose pairing. "generalize is a composition" then reads naturally.

Step 2 — generalize sugar (moderate). Plain-call sugar constructing a Compose action as above, plus the identity tag and the coarsen/review/render recognition work. Author CLI verb can follow in a second phase.

Non-goals. No claim-level structural compose (low value — the equivalence is already bidirectional). decompose unchanged. The deliberate non-duality of decompose (equivalence, no information gain, no warrant needed) vs generalize (ampliative, inductive risk, mandatory warrant) is an epistemological fact the language should surface, not paper over with a morphologically symmetric word pair.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions