Metadata-Version: 2.4
Name: proact-py
Version: 0.1.0
Summary: Active logic meets Prolog — three-valued reasoning engine
License-Expression: MIT
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"

# Proact

Prolog has two truth values: succeed and fail. Proact adds a third: **cont** — *work/reasoning in progress*.

Every goal returns a status. Conjunction is a sequence. Disjunction is a selector. Backtracking only triggers on failure — `cont` and `done` cut the search. Between ticks, queries returning `cont` are automatically re-evaluated while the database persists, giving you a stateless tick loop for free.

Applications:

- **Reasoning under uncertainty.** The three values map onto epistemic states: *verified*, *undetermined*, *refuted*. A conjunction of checks naturally implements iterative deepening — work focuses on the earliest unresolved premise. Unary operators give you cognitive control: `demote` means "don't commit yet" (done→cont); `promote` means "don't give up yet" (fail→cont). This is relevant to validation layers for LLM reasoning, where forcing every check into true/false is lossy compression of the actual epistemic state.

- **Behavior modeling.** Reactive agents, planners, robot controllers — anything where logic meets time. Prolog gives you unification, backtracking, and pattern matching. Active logic adds temporal flow. You get behavior trees with the expressiveness of logic programming, without the trees.

## Install

```
pip install git+https://github.com/eelstork/proact.git
```

Requires a C compiler (`gcc`, `clang`, or MSVC).

```python
from proact import Proact

p = Proact()
p.add("mortal(X) :- human(X). human(socrates).")
print(p.query("mortal(socrates)"))  # Result(status='done')
```

## Tutorials

- [Reasoning under uncertainty](tutorial/reasoning.md) — from predicate logic to three-valued epistemics
- [Behavior modeling](tutorial/behavior.md) — behavior trees via logic programming

## Build

```
make
```

## Usage

```
./proact                          # REPL
./proact file.pa                  # consult file, then REPL
./proact file.pa -e "goal."      # evaluate goal and exit
./proact --max-ticks 100 -e "g." # limit tick count
```

## The Transform

### Conjunction (`,`) — Active logic AND / Sequence

```
         fail  cont  done
  fail   fail  fail  fail
  cont   cont  cont  cont
  done   fail  cont  done
```

If the left goal is `cont`, the whole conjunction is `cont` — don't evaluate the right side yet.

### Disjunction (`;`) — Active logic OR / Selector

```
         fail  cont  done
  fail   fail  cont  done
  cont   cont  cont  cont
  done   done  done  done
```

If the left goal is `done` or `cont`, don't try alternatives.

### Clause resolution

Multiple clauses for the same predicate provide choice points. Backtracking occurs **only on fail** — if a clause body returns `cont` or `done`, stop searching.

### Tick loop

Queries returning `cont` are automatically re-evaluated on the next tick with a fresh environment. The database (asserted facts) persists between ticks.

## Built-ins

| Predicate | Status | Description |
|---|---|---|
| `done` / `true` | done | Always succeeds |
| `fail` / `false` | fail | Always fails |
| `cont` | cont | Always continues |
| `inv(G)` / `\+ G` | swap done/fail, keep cont | Inverter |
| `condone(G)` | fail→done | Forgive failure |
| `promote(G)` | fail→cont, cont→done | Optimistic |
| `demote(G)` | done→cont, cont→fail | Pessimistic |
| `par_any(A,B)` | max(A,B) | Lenient parallel |
| `par_all(A,B)` | min(A,B) | Strict parallel |
| `tick(N)` | done | Unify N with current tick |

Plus standard Prolog: `write/1`, `writeln/1`, `nl`, `assert/1`, `retract/1`, `is/2`, `=/2`, `\=/2`, `</2`, `>/2`, `>=/2`, `=</2`, `call/1`, `consult/1`.

## Examples

### Wait N ticks, then succeed

```prolog
wait(N) :- tick(T), T >= N.
wait(N) :- tick(T), T < N, cont.
```

```
?- wait(3), writeln(hello).
[tick 0] cont
[tick 1] cont
[tick 2] cont
hello
done.
```

### Counter (stateful, using assert/retract)

```prolog
:- assert(count(0)).

count_to(Target) :- count(N), N >= Target.
count_to(Target) :-
    count(N), N < Target,
    retract(count(N)), N1 is N + 1, assert(count(N1)),
    cont.
```

### Reactive behavior (condition-gated sequence)

```prolog
:- assert(agent_pos(0)).

move_to(X) :- agent_pos(P), P =:= X.
move_to(X) :- agent_pos(P), P < X,
    retract(agent_pos(P)), P1 is P + 1, assert(agent_pos(P1)), cont.

patrol :-
    agent_pos(P), P < 3, move_to(3), cont.
patrol :-
    agent_pos(P), P >= 3, P < 5, move_to(5), cont.
patrol :-
    agent_pos(P), P >= 5.
```

### Reasoning under uncertainty

```prolog
% reason.pa — three-valued validation for multi-step arguments

:- assert(known(premise_a)).
:- assert(investigation(premise_b, 2)).
:- assert(investigation(premise_c, 1)).

check(P) :- known(P).
check(P) :- known_false(P), fail.
check(P) :-
    investigation(P, N), N > 0,
    retract(investigation(P, N)), N1 is N - 1,
    (N1 > 0 -> assert(investigation(P, N1)) ; assert(known(P))),
    cont.

argument :- check(premise_a), check(premise_b), check(premise_c).
```

The conjunction *is* the search strategy — it focuses work on the earliest unresolved premise and doesn't waste computation on steps whose preconditions haven't been met:

```
[tick 0] a=done, b=cont          → cont  (c never reached)
[tick 1] a=done, b=done, c=cont  → cont  (work shifts to c)
[tick 2] a=done, b=done, c=done  → done
```

The epistemic operators give cognitive control over the validation:

```prolog
cautious(X)       :- demote(check(X)).    % "verified but needs review"  (done→cont)
keep_exploring(X) :- promote(check(X)).   % "refuted but try harder"     (fail→cont)
viable_theory     :- par_any(hyp_a, hyp_b). % first hypothesis verified wins
```

## Relation to active-logic

This prototype implements the core operators from [activelogic-cs](https://github.com/active-logic/activelogic-cs) in a logic programming context:

| C# (activelogic-cs) | Proact | BT equivalent |
|---|---|---|
| `status && status` | `Goal, Goal` | Sequence |
| `status \|\| status` | `Goal ; Goal` | Selector |
| `+` (lenient) | `par_any(A, B)` | Parallel (any) |
| `*` (strict) | `par_all(A, B)` | Parallel (all) |
| `!status` | `inv(G)` | Inverter |
| `~status` | `condone(G)` | Condone |
| `+status` (unary) | `promote(G)` | Promoter |
| `-status` (unary) | `demote(G)` | Demoter |
| `cont` | `cont` | Running |

The key addition over Prolog is `cont` — a single built-in that bridges logic programming with reactive, tick-based behavior.
