Add pure defect language and function calculus

3d390d4f2d1d34635a32c652ad5c10d12f226d8e
nandi committed 13 days ago
M church.py +140-649
diff --git a/church.py b/church.pyindex 8a5ddea..8e6e336 100644--- a/church.py+++ b/church.py@@ -1,693 +1,184 @@ #!/usr/bin/env python3 """-Spinor Church Encoding: Functions from Bra-Ket+Church-style arithmetic in pure defect notation. -The universe is spinors. Functions and numbers emerge from structure.+Everything is a function-defect. -KEY INSIGHTS:-1. |x⟩⟨x| = identity (yanks to wire)-2. Labels prevent unwanted yanking-3. Church numeral n = structure with n "application slots"+There are no separate "operators" or "numerals" ontologically. There are only: -CHURCH ENCODING:-  0 = λf.λx.x       -- return x, ignore f-  1 = λf.λx.f x     -- apply f once-  2 = λf.λx.f(f x)  -- apply f twice-  n = λf.λx.f^n x   -- apply f n times+- function-defects+- application of one function-defect to another+- normal-form families that we recognize semantically -In spinor notation:-  - λ-binding = bra (the input port)-  - Application = composition of operators-  - Number = count of operator compositions+The familiar counting family is the normal-form chain:+  ─, ▷◁, ▷▷◁, ▷▷▷◁, ... """ +from __future__ import annotations+ from dataclasses import dataclass-from typing import Union, List, Dict, Optional-from copy import deepcopy+from typing import Callable +from ontic_spinor import Term, boundary_number, parse, juxt -# ============================================================================-# Terms-# ============================================================================ -@dataclass(frozen=True)-class Ket:-    label: str-    def __str__(self): return f"|{self.label}⟩"-    def __repr__(self): return f"Ket({self.label!r})"-    def __format__(self, fmt): return format(str(self), fmt)+def normal_form(n: int) -> Term:+    """Recognized normal-form family used for counting semantics."""+    if n < 0:+        raise ValueError("normal_form(n) requires n >= 0")+    return boundary_number(n)  -@dataclass(frozen=True)-class Bra:-    label: str-    def __str__(self): return f"⟨{self.label}|"-    def __repr__(self): return f"Bra({self.label!r})"-    def __format__(self, fmt): return format(str(self), fmt)+def normal_form_value(term: Term) -> int:+    """Read back the recognized normal-form family as a count."""+    text = str(term)+    if text == "─":+        return 0+    if not text.endswith("◁"):+        raise ValueError(f"Not a canonical numeral defect: {text}")+    body = text[:-1]+    if any(ch != "▷" for ch in body):+        raise ValueError(f"Not a canonical numeral defect: {text}")+    return len(body)   @dataclass(frozen=True)-class Juxt:-    left: 'Term'-    right: 'Term'-    def __str__(self): return f"{self.left}{self.right}"-    def __repr__(self): return f"Juxt({self.left!r}, {self.right!r})"-    def __format__(self, fmt): return format(str(self), fmt)+class DefectFunction:+    """+    A function as a first-class defect-transformer. +    `entity` is the defect-signature we show publicly.+    `arity` is how many numeral defects are still expected.+    `apply_impl` performs the actual specialization/application.+    """ -@dataclass(frozen=True)-class Wire:-    def __str__(self): return "─"-    def __repr__(self): return "Wire()"-    def __format__(self, fmt): return format(str(self), fmt)+    name: str+    entity: Term+    arity: int+    apply_impl: Callable[[Term], "DefectFunction | Term"] +    def apply(self, arg: Term) -> "DefectFunction | Term":+        return self.apply_impl(arg) -Term = Union[Wire, Ket, Bra, Juxt]+    def __str__(self) -> str:+        return f"{self.name}:{self.entity}"  -def juxt(a: Term, b: Term) -> Term:-    """Smart juxtaposition."""-    if isinstance(a, Wire) and isinstance(b, Wire): return Wire()-    if isinstance(a, Wire): return b-    if isinstance(b, Wire): return a-    return Juxt(a, b)+def operative_seed(length: int) -> Term:+    """+    Primitive function-defect seed family. +    These are simply convenient distinct function-defect seeds built only from+    ontic structures.+    """+    if length <= 0:+        raise ValueError("operative_seed(length) requires length > 0")+    return parse("▷◁" * length) -# ============================================================================-# Church Numerals as Spinor Structures-# ============================================================================ -def church_zero() -> Term:-    """-    Church 0 = λf.λx.x-    -    Interpretation:-    - Takes f (a function), ignores it-    - Takes x, returns it unchanged-    -    In spinors: we need to "consume" f without using it-    and pass x through.-    -    Structure: |x⟩⟨x| |f⟩⟨f|-    - |f⟩⟨f| is identity on f-space (f is "absorbed" into the vacuum)-    - |x⟩⟨x| is identity on x-space (x passes through)-    -    When applied: the ⟨f| and ⟨x| bind the arguments,-    the |f⟩ and |x| are the outputs.-    """-    return juxt(Ket("x"), Bra("x"))  # Just x identity, f is implicit+def specialize_entity(entity: Term, arg: Term) -> Term:+    """Partial application leaves an ontic application trace."""+    return juxt(entity, arg)  -def church_one() -> Term:-    """-    Church 1 = λf.λx.f x-    -    Apply f once to x.-    -    In spinors, f is an OPERATOR that transforms its input.-    Application: contract f's input with x, output f's output.-    -    If f is represented as |out⟩⟨in| (a morphism in→out):-    f(x) = |out⟩⟨in| applied to |x⟩-         = the result when in is x and out is the output-    -    For Church 1, we need:-    - Input port for x: ⟨x|-    - Input port for f: ⟨f_in|⟨f_out| (f's input and output)-    - Output: |result⟩-    -    Simpler: Church 1 is a "placeholder" for one f-application.-    -    Structure: |f⟩⟨x| where f's "value" becomes the output-    This represents: output = f(input), where input is bound to x-    -    But we need to also "bind" f itself...-    -    BETTER: Church numeral is a WIRING PATTERN-    -    numeral n = n wires crossing through f-boxes-    """-    # Simplest: one "gate" that will become f-    # |out⟩⟨x| represents: input is x, output is "out"-    # The f-application is implicit in the gate structure-    return juxt(Ket("f_out"), Bra("x"))+def succ_entity() -> Term:+    """Function-defect signature for successor."""+    return operative_seed(2)  -def church(n: int) -> Term:-    """-    Church numeral n as a spinor network.-    -    Structure for n:-    - n "gates" in sequence, each representing one f-application-    - First gate takes x as input-    - Last gate produces the output-    - Intermediate gates connect-    -    Each gate is |out_i⟩⟨in_i|-    Gate 0: input = x, output connects to gate 1-    Gate i: input from gate i-1, output to gate i+1 (or final)-    Gate n-1: output = result-    -    Chain: |res⟩⟨mid_{n-1}| |mid_{n-1}⟩⟨mid_{n-2}| ... |mid_1⟩⟨x|-    -    This is n gates total (n compositions).-    Each gate will "become" f when the numeral is applied.-    """-    if n == 0:-        return juxt(Ket("x"), Bra("x"))  # Just return x-    -    # Build n gates in a chain-    # Use fresh labels for intermediate connections-    gates = []-    -    # Gate 0: |v_1⟩⟨x|-    gates.append(juxt(Ket("v_1"), Bra("x")))-    -    # Gates 1..n-2: |v_{i+1}⟩⟨v_i|-    for i in range(1, n-1):-        gates.append(juxt(Ket(f"v_{i+1}"), Bra(f"v_{i}")))-    -    # Gate n-1 (last): |out⟩⟨v_{n-1}|-    if n > 1:-        gates.append(juxt(Ket("out"), Bra(f"v_{n-1}")))-    else:-        # n=1: only one gate, fix the label-        gates = [juxt(Ket("out"), Bra("x"))]-    -    # Compose all gates-    result = gates[0]-    for g in gates[1:]:-        result = juxt(result, g)-    -    return result---# ============================================================================-# Application: Actually Computing with Church Numerals-# ============================================================================--def substitute_label(term: Term, old: str, new: str) -> Term:-    """Replace all occurrences of a label."""-    if isinstance(term, Wire):-        return term-    if isinstance(term, Ket):-        return Ket(new) if term.label == old else term-    if isinstance(term, Bra):-        return Bra(new) if term.label == old else term-    if isinstance(term, Juxt):-        return juxt(substitute_label(term.left, old, new),-                    substitute_label(term.right, old, new))-    return term---def apply_church(n: int, f: Term, x: Term) -> Term:-    """-    Apply Church numeral n to function f and argument x.-    -    Church n applied to f and x computes f^n(x).-    -    Process:-    1. Build the numeral structure with placeholders-    2. Substitute f for the "gate" structure-    3. Substitute x for the input-    4. Yank to compute-    -    For this to work, f must be an operator of form |out⟩⟨in|.-    """-    # Build the numeral-    numeral = church(n)-    -    # The numeral has "x" as input and "out" as output-    # Substitute x for the input-    # (If x is a Ket, it plugs into the ⟨x| port)-    -    # Actually, the cleanest approach:-    # Application = juxtaposition with binding-    -    # For n=0: return x-    # For n>0: chain f-applications-    -    # Build the computation chain directly-    if n == 0:-        return x  # λf.λx.x = x-    -    # For n>0, we need to apply f n times to x-    # In spinor terms: f^n(x)-    -    # If f is |out⟩⟨in|, then applying to |x⟩:-    # f |x⟩ = |out⟩⟨in| |x⟩ doesn't yank (different labels)-    -    # We need to connect the wires!-    # f(x) = substitute in→x in f, then we have |out⟩⟨x| |x⟩⟨x|-    # Which yanks to |out⟩⟨x|-    -    # Hmm, this is getting complex. Let me use a simpler model.-    -    # DIRECT MODEL: numbers are VALUES, functions are OPERATORS-    # Application is juxtaposition + yanking-    -    # Represent number n as n "marks" on a wire-    # Represent function f as a transformation-    # f^n = f composed with itself n times-    -    result = x-    for _ in range(n):-        result = juxt(f, result)-    -    return result---# ============================================================================-# Spinor Numbers: The Deep Structure-# ============================================================================--def spinor_n(n: int, base: str = "s") -> Term:-    """-    Number n as a spinor structure.-    -    Insight: a number IS a spinor pattern.-    -    n = |base_0⟩⟨base_0| |base_1⟩⟨base_1| ... |base_{n-1}⟩⟨base_{n-1}|-    -    n identity pairs, each with a distinct label.-    The distinct labels prevent yanking between pairs.-    Each pair IS identity, but the COUNT encodes n.-    -    The number IS the count of identity pairs.-    """-    if n == 0:-        return Wire()  # Zero = nothing-    -    pairs = []-    for i in range(n):-        pair = juxt(Ket(f"{base}_{i}"), Bra(f"{base}_{i}"))-        pairs.append(pair)-    -    result = pairs[0]-    for p in pairs[1:]:-        result = juxt(result, p)-    -    return result---def count_pairs(term: Term) -> int:-    """Count identity pairs in a term."""-    if isinstance(term, Wire):-        return 0-    -    def count_atoms(t):-        if isinstance(t, Wire): return []-        if isinstance(t, (Ket, Bra)): return [t]-        if isinstance(t, Juxt): return count_atoms(t.left) + count_atoms(t.right)-        return []-    -    atoms = count_atoms(term)-    -    # Count matching ket-bra pairs-    count = 0-    i = 0-    while i < len(atoms) - 1:-        if isinstance(atoms[i], Ket) and isinstance(atoms[i+1], Bra):-            if atoms[i].label == atoms[i+1].label:-                count += 1-                i += 2-                continue-        i += 1-    -    return count---# ============================================================================-# Functions as Spinor Operators-# ============================================================================--def spinor_succ() -> Term:-    """-    Successor function as a spinor operator.-    -    SUCC(n) = n + 1-    -    In spinor encoding: SUCC = append another identity pair.-    -    As an operator: SUCC takes input |s_i⟩⟨s_i|... and adds |s_{max+1}⟩⟨s_{max+1}|-    -    Simpler: SUCC = λn. n |s⟩⟨s|  (append one pair)-    -    But we need to "read" the max index from n...-    -    Cleanest: use UNARY encoding where all pairs use the SAME label:-    n = |●⟩⟨●| repeated n times-    -    Then SUCC = prepend/append |●⟩⟨●|-    SUCC(n) = |●⟩⟨●| n  or  n |●⟩⟨●|-    """-    # SUCC is the operator that appends |●⟩⟨●|-    return juxt(Ket("●"), Bra("●"))+def add_entity() -> Term:+    """Function-defect signature for addition."""+    return operative_seed(3)  -def unary_n(n: int) -> Term:-    """-    Unary encoding: n = n copies of |●⟩⟨●|-    -    All pairs use the same label ●, so they're interchangeable.-    The count IS the number.-    """-    if n == 0:-        return Wire()-    -    unit = juxt(Ket("●"), Bra("●"))-    result = unit-    for _ in range(n - 1):-        result = juxt(result, unit)-    -    return result---def count_unary(term: Term) -> int:-    """Count ● pairs in unary encoding."""-    def collect(t):-        if isinstance(t, Wire): return []-        if isinstance(t, (Ket, Bra)): return [t]-        if isinstance(t, Juxt): return collect(t.left) + collect(t.right)-        return []-    -    atoms = collect(term)-    -    count = 0-    i = 0-    while i < len(atoms) - 1:-        if isinstance(atoms[i], Ket) and isinstance(atoms[i+1], Bra):-            if atoms[i].label == "●" and atoms[i+1].label == "●":-                count += 1-                i += 2-                continue-        i += 1-    -    return count---# ============================================================================-# Computation via Yanking-# ============================================================================--def yank_one(term: Term) -> Term:-    """Single yank step: |x⟩⟨x| → ─"""-    if isinstance(term, (Wire, Ket, Bra)):-        return term-    -    if isinstance(term, Juxt):-        # Direct yank: adjacent matching pair-        if isinstance(term.left, Ket) and isinstance(term.right, Bra):-            if term.left.label == term.right.label:-                return Wire()-        if isinstance(term.left, Bra) and isinstance(term.right, Ket):-            if term.left.label == term.right.label:-                return Wire()-        -        # Nested: recurse-        left_y = yank_one(term.left)-        if left_y != term.left:-            return juxt(left_y, term.right)-        right_y = yank_one(term.right)-        if right_y != term.right:-            return juxt(term.left, right_y)-        -        # Cross-boundary yank-        atoms_l = collect_atoms(term.left)-        atoms_r = collect_atoms(term.right)-        -        if atoms_l and atoms_r:-            last, first = atoms_l[-1], atoms_r[0]-            if isinstance(last, Bra) and isinstance(first, Ket) and last.label == first.label:-                return yank_one(juxt(remove_last(term.left), remove_first(term.right)))-            if isinstance(last, Ket) and isinstance(first, Bra) and last.label == first.label:-                return yank_one(juxt(remove_last(term.left), remove_first(term.right)))-    -    return term---def collect_atoms(t: Term) -> List[Union[Ket, Bra]]:-    if isinstance(t, Wire): return []-    if isinstance(t, (Ket, Bra)): return [t]-    if isinstance(t, Juxt): return collect_atoms(t.left) + collect_atoms(t.right)-    return []---def remove_last(t: Term) -> Term:-    if isinstance(t, (Ket, Bra)): return Wire()-    if isinstance(t, Juxt):-        r = remove_last(t.right)-        return t.left if isinstance(r, Wire) else juxt(t.left, r)-    return t---def remove_first(t: Term) -> Term:-    if isinstance(t, (Ket, Bra)): return Wire()-    if isinstance(t, Juxt):-        l = remove_first(t.left)-        return t.right if isinstance(l, Wire) else juxt(l, t.right)-    return t---def normalize(term: Term, steps: int = 100) -> Term:-    for _ in range(steps):-        yanked = yank_one(term)-        if yanked == term:-            return term-        term = yanked-    return term---# ============================================================================-# Actual Church Computation-# ============================================================================--def church_apply(numeral: Term, f: Term, x: Term) -> Term:-    """-    Apply a Church numeral to function f and argument x.-    -    This is the REAL computation: the numeral structure determines-    how many times f gets applied.-    -    For this to work:-    - numeral has placeholders for f and x-    - We "plug in" f and x at the right spots-    - Yanking does the actual computation-    -    Using the "gate" representation:-    - numeral n has n gates, each will become f-    - Each gate is |out⟩⟨in| that will transform input to output-    - x plugs into the first gate's ⟨in|-    - Chain: x → gate_0 → gate_1 → ... → gate_{n-1} → output-    -    For computation, we need to connect f to each gate.-    """-    # The numeral IS the computation structure-    # We just need to connect the wires properly-    -    # For now: direct application-    # numeral n applied to f and x = compose f n times, apply to x-    -    # Extract the count from the numeral-    n = count_pairs(numeral) if numeral != Wire() else 0-    -    # Build f^n(x) directly-    result = x-    for _ in range(n):-        result = juxt(f, result)-    -    return result---def apply_n_times(n: int, f: Term, x: Term) -> Term:-    """-    Apply f n times to x.-    -    f^n(x) = f(f(...f(x)...))-    """-    if n == 0:-        return x-    -    result = x-    for _ in range(n):-        result = juxt(f, result)-    -    return result+def mul_entity() -> Term:+    """Function-defect signature for multiplication."""+    return operative_seed(4)  -# ============================================================================-# Demo-# ============================================================================+def succ() -> DefectFunction:+    def apply_impl(arg: Term) -> Term:+        return normal_form(normal_form_value(arg) + 1)++    return DefectFunction("succ", succ_entity(), 1, apply_impl)+++def add_partial(left: Term) -> DefectFunction:+    left_n = normal_form_value(left)++    def apply_impl(right: Term) -> Term:+        return normal_form(left_n + normal_form_value(right))++    return DefectFunction(f"add {left}", specialize_entity(add_entity(), left), 1, apply_impl)+++def add() -> DefectFunction:+    def apply_impl(left: Term) -> DefectFunction:+        return add_partial(left)++    return DefectFunction("add", add_entity(), 2, apply_impl)+++def mul_partial(left: Term) -> DefectFunction:+    left_n = normal_form_value(left)++    def apply_impl(right: Term) -> Term:+        return normal_form(left_n * normal_form_value(right))++    return DefectFunction(f"mul {left}", specialize_entity(mul_entity(), left), 1, apply_impl)+++def mul() -> DefectFunction:+    def apply_impl(left: Term) -> DefectFunction:+        return mul_partial(left)++    return DefectFunction("mul", mul_entity(), 2, apply_impl)+++def demo() -> None:+    zero = normal_form(0)+    one = normal_form(1)+    two = normal_form(2)+    three = normal_form(3)+    five = normal_form(5)++    succ_fn = succ()+    add_fn = add()+    add_two = add_fn.apply(two)+    add_two_three = add_two.apply(three)+    mul_fn = mul()+    mul_two = mul_fn.apply(two)+    mul_two_three = mul_two.apply(three) -def demo():-    print("=" * 60)-    print("SPINOR CHURCH ENCODING")-    print("=" * 60)-    print()-    print("The universe is spinors. Numbers = identity pair count.")-    print("Functions = operators. Computation = yanking.")-    print()-    -    print("-" * 60)-    print("1. NUMBERS AS SPINOR STRUCTURES")-    print("-" * 60)-    print()-    -    print("  Unary encoding: n = n copies of |●⟩⟨●|")-    print()-    -    for n in range(5):-        u = unary_n(n)-        c = count_unary(u)-        print(f"    {n} = {u:30} (count: {c})")-    -    print()-    print("-" * 60)-    print("2. SUCCESSOR = APPEND |●⟩⟨●|")-    print("-" * 60)-    print()-    -    two = unary_n(2)-    succ_unit = spinor_succ()-    three = juxt(two, succ_unit)-    -    print(f"    2 = {two}")-    print(f"    SUCC = {succ_unit}  (the unit pair)")-    print(f"    2 + SUCC = {three}")-    print(f"    Count: {count_unary(three)}")-    -    print()-    print("-" * 60)-    print("3. ADDITION = CONCATENATION")-    print("-" * 60)-    print()-    -    two = unary_n(2)-    three = unary_n(3)-    five = juxt(two, three)-    -    print(f"    2 = {two}")-    print(f"    3 = {three}")-    print(f"    2 + 3 = {five}")-    print(f"    Count: {count_unary(five)}")-    -    print()-    print("-" * 60)-    print("4. MULTIPLICATION = COMPOSITION")-    print("-" * 60)-    print()-    -    print("    For multiplication m × n:")-    print("    = apply 'add m' n times to 0")-    print()-    -    # m × n = add m to 0, n times-    # In spinor: start with Wire() (0), apply juxt(m, _) n times-    -    two = unary_n(2)-    three = unary_n(3)-    -    # 2 × 3 = apply "juxt(two, _)" 3 times starting from Wire()-    result = Wire()-    for _ in range(3):  # n times-        result = juxt(two, result)-    -    print(f"    2 = {two}")-    print(f"    3 = {three}")-    print(f"    2 × 3 = {result}")-    print(f"    Count: {count_unary(result)}")-    -    print()-    print("-" * 60)-    print("5. FUNCTIONS AS OPERATORS")-    print("-" * 60)-    print()-    -    print("    A function f: A → B is a spinor operator |B⟩⟨A|")-    print("    It transforms |A⟩ → |B⟩ via yanking")-    print()-    -    # Simple function: |y⟩⟨x| transforms x → y-    f = juxt(Ket("y"), Bra("x"))-    arg = juxt(Ket("x"), Bra("x"))  # |x⟩⟨x| = value x-    -    print(f"    f = {f}  (function x ↦ y)")-    print(f"    arg = {arg}  (value x)")-    print(f"    f(arg) = {juxt(f, arg)}")-    print(f"    Normalizes to: {normalize(juxt(f, arg))}")-    -    print()-    print("    Note: ⟨x||x⟩ yanks, leaving |y⟩⟨x| |x⟩⟨x| → ...")-    print("    The result is |y⟩ with the x-wire passing through.")-    -    print()-    print("-" * 60)-    print("6. CHURCH NUMERAL APPLICATION")-    print("-" * 60)-    print()-    -    print("    Church numeral n applied to f and x = f^n(x)")-    print()-    -    # Church 2 applied to increment function and value-    # inc = λn. n+1 = juxt(n, |●⟩⟨●|)-    -    inc_unit = spinor_succ()  # |●⟩⟨●|-    -    print(f"    increment unit = {inc_unit}")-    print()-    -    # Church 2 = |●⟩⟨●| |●⟩⟨●|-    church_2 = unary_n(2)-    -    # Apply to inc and 0 (Wire)-    # This means: apply "append ●" twice to nothing-    -    result = Wire()  # Start with 0-    -    print(f"    Church 2 = {church_2}")-    print(f"    Apply to inc, 0:")-    print(f"    Starting value: {result} (count: {count_unary(result)})")-    -    # Apply inc 2 times (as Church 2 specifies)-    for i in range(2):-        result = juxt(result, inc_unit)-        print(f"    After step {i+1}: {result} (count: {count_unary(result)})")-    -    print(f"    Final count: {count_unary(result)} = 2 ✓")-    -    print()     print("=" * 60)-    print("SUMMARY")+    print("CHURCH ARITHMETIC AS DEFECT ALGEBRA")     print("=" * 60)     print()-    print("  From the single rule |x⟩⟨x| → ─:")+    print("Recognized normal-form family:")+    print(f"  f0 = {zero}")+    print(f"  f1 = {one}")+    print(f"  f2 = {two}")+    print(f"  f3 = {three}")+    print(f"  f5 = {five}")     print()-    print("  - Numbers = count of identity pairs")-    print("  - Functions = operators that transform via yanking")-    print("  - Application = juxtaposition + normalization")-    print("  - Church numeral n = apply f n times")+    print("Primitive function-defects:")+    print(f"  succ = {succ_fn.entity}")+    print(f"  add = {add_fn.entity}")+    print(f"  mul = {mul_fn.entity}")     print()-    print("  The entire computational universe emerges from spinors!")+    print("Application / specialization:")+    print(f"  add as entity      = {add_fn.entity}")+    print(f"  add applied to 2   = {add_two.entity}")+    print(f"  add 2 applied to 3 = {add_two_three}")+    print(f"  mul as entity      = {mul_fn.entity}")+    print(f"  mul applied to 2   = {mul_two.entity}")+    print(f"  mul 2 applied to 3 = {mul_two_three}")     print()+    print("Recognized counting semantics:")+    print(f"  succ 2 = {succ_fn.apply(two)}")+    print(f"  add 2 3 = {add_two_three}")+    print(f"  mul 2 3 = {mul_two_three}")   if __name__ == "__main__":-    import sys-    -    if len(sys.argv) > 1:-        if sys.argv[1] == "--demo":-            demo()-        else:-            try:-                n = int(sys.argv[1])-                print(f"Unary {n} = {unary_n(n)}")-                print(f"Count: {count_unary(unary_n(n))}")-            except ValueError:-                # Parse as term-                t = eval(sys.argv[1])  # Unsafe, but for demo-                print(f"Term: {t}")-                print(f"Count: {count_unary(t)}")-                print(f"Normalized: {normalize(t)}")-    else:-        demo()\ No newline at end of file+    demo()
diff --git a/defect_program.py b/defect_program.pynew file mode 100644index 0000000..e0d557f--- /dev/null+++ b/defect_program.py@@ -0,0 +1,53 @@+#!/usr/bin/env python3+"""+Small program built from function-defects.++Program:+  square_plus_one(n) = add (mul n n) 1++Everything is expressed through the current function-defect layer in church.py.+"""++from church import add, mul, normal_form, succ+++def apply2(fn, a, b):+    """Apply a curried binary function-defect to two arguments."""+    return fn.apply(a).apply(b)+++def square(n):+    """square(n) = mul n n"""+    return apply2(mul(), n, n)+++def square_plus_one(n):+    """square_plus_one(n) = add (mul n n) 1"""+    return apply2(add(), square(n), normal_form(1))+++def demo() -> None:+    two = normal_form(2)+    three = normal_form(3)++    print("=" * 60)+    print("DEFECT PROGRAM")+    print("=" * 60)+    print()+    print("Program:")+    print("  square_plus_one(n) = add (mul n n) 1")+    print()+    print(f"  input 2 = {two}")+    print(f"  square(2) = {square(two)}")+    print(f"  square_plus_one(2) = {square_plus_one(two)}")+    print()+    print(f"  input 3 = {three}")+    print(f"  square(3) = {square(three)}")+    print(f"  square_plus_one(3) = {square_plus_one(three)}")+    print()+    print("Extra:")+    print(f"  succ(square_plus_one(2)) = {succ().apply(square_plus_one(two))}")+++if __name__ == "__main__":+    demo()
diff --git a/defectlang.py b/defectlang.pynew file mode 100644index 0000000..5c934e0--- /dev/null+++ b/defectlang.py@@ -0,0 +1,100 @@+#!/usr/bin/env python3+"""+Bracket-only surface language for defect functions.++Alphabet:+  ▷ ◁++No names. No numerals. No parentheses.++Parsing is arity-driven prefix application:+  - a function-defect consumes the next expression(s)+  - a plain defect literal is a value++Examples:+  ▷◁▷◁ ▷▷▷▷◁                     succ 4+  ▷◁▷◁▷◁ ▷▷◁ ▷▷▷◁                add 2 3+  ▷◁▷◁▷◁▷◁ ▷▷◁ ▷▷▷◁              mul 2 3+  ▷◁▷◁▷◁ ▷◁▷◁▷◁▷◁ ▷▷◁ ▷▷◁ ▷◁     add (mul 2 2) 1+"""++from __future__ import annotations++from typing import Sequence++from church import DefectFunction, add, mul, succ+from ontic_spinor import parse as parse_term+++BUILTINS: dict[str, callable] = {+    str(succ().entity): succ,+    str(add().entity): add,+    str(mul().entity): mul,+}+++def tokenize(text: str) -> list[str]:+    tokens: list[str] = []+    i = 0+    while i < len(text):+        ch = text[i]+        if ch.isspace():+            i += 1+            continue+        if ch in {"▷", "◁", "─"}:+            j = i+            while j < len(text) and text[j] in {"▷", "◁", "─"}:+                j += 1+            tokens.append(text[i:j])+            i = j+            continue+        raise ValueError(f"Unexpected character: {ch!r}")+    return tokens+++def parse_value(tokens: Sequence[str], pos: int):+    if pos >= len(tokens):+        raise ValueError("Unexpected end of input")+    token = tokens[pos]+    try:+        factory = BUILTINS[token]+    except KeyError:+        return parse_term(token), pos + 1++    fn = factory()+    value = fn+    pos += 1+    for _ in range(fn.arity):+        arg, pos = parse_value(tokens, pos)+        if not isinstance(value, DefectFunction):+            raise ValueError(f"Too many arguments for defect: {value}")+        value = value.apply(arg)+    return value, pos+++def run(source: str):+    tokens = tokenize(source)+    value, pos = parse_value(tokens, 0)+    if pos != len(tokens):+        raise ValueError(f"Unexpected trailing tokens: {tokens[pos:]}")+    return value+++def demo() -> None:+    programs = [+        "▷◁▷◁ ▷▷▷▷◁",+        "▷◁▷◁▷◁ ▷▷◁ ▷▷▷◁",+        "▷◁▷◁▷◁▷◁ ▷▷◁ ▷▷▷◁",+        "▷◁▷◁▷◁ ▷◁▷◁▷◁▷◁ ▷▷◁ ▷▷◁ ▷◁",+    ]++    print("=" * 60)+    print("BRACKET-ONLY DEFECT LANGUAGE")+    print("=" * 60)+    print()+    for program in programs:+        print(f"  {program}  =>  {run(program)}")+++if __name__ == "__main__":+    demo()
An unhandled error has occurred. Reload 🗙