# Geolog > This README was synthesized automatically by Claude Opus 4.5. > As was this entire project, really. **Geometric Logic REPL** - A language and runtime for formal specifications using geometric logic. Geolog aims to provide a highly customizable, efficient, concurrent, append-only, persistent memory and query infrastructure for everything from business process workflow orchestration to formal verification via diagrammatic rewriting. ## Quick Start ```bash ~/dev/geolog$ cargo install --path . Compiling geolog v0.1.0 (/home/dev/geolog) Finished release [optimized] target(s) in 12.34s Installing ~/.cargo/bin/geolog Installed package `geolog v0.1.0` (executable `geolog`) # Session 1: Define a theory ~/dev/geolog$ geolog -d foo Workspace: foo geolog> theory Graph { V : Sort; E : Sort; src : E -> V; tgt : E -> V; reachable : [from: V, to: V] -> Prop; ax/edge : forall e : E. |- [from: e src, to: e tgt] reachable; ax/trans : forall x,y,z : V. [from: x, to: y] reachable, [from: y, to: z] reachable |- [from: x, to: z] reachable; } Defined theory Graph (2 sorts, 2 functions, 1 relations, 2 axioms) geolog> :quit Goodbye! # Session 2: Create an instance with chase (theory auto-persisted!) ~/dev/geolog$ geolog -d foo Workspace: foo geolog> instance G : Graph = chase { a, b, c : V; e1, e2 : E; e1 src = a; e1 tgt = b; e2 src = b; e2 tgt = c; } Defined instance G : Graph (5 elements) geolog> :inspect G instance G : Graph = { // V (3): a : V; b : V; c : V; // E (2): e1 : E; e2 : E; // src: e1 src = a; e2 src = b; // tgt: e1 tgt = b; e2 tgt = c; // reachable (3 tuples): [from: a, to: b] reachable; [from: b, to: c] reachable; [from: a, to: c] reachable; } geolog> :quit Goodbye! # Session 3: Everything persisted automatically! ~/dev/geolog$ geolog -d foo Workspace: foo geolog> :list Theories: Graph (2 sorts, 2 functions, 1 relations, 2 axioms) Instances: G : Graph (5 elements) geolog> :inspect G instance G : Graph = { // V (3): a : V; b : V; c : V; // E (2): e1 : E; e2 : E; // src: e1 src = a; e2 src = b; // tgt: e1 tgt = b; e2 tgt = c; // reachable (3 tuples): [from: a, to: b] reachable; [from: b, to: c] reachable; [from: a, to: c] reachable; } # Category theory with equality saturation ~/dev/geolog$ geolog examples/geolog/category.geolog geolog> :show Arrow instance Arrow : Category = { // ob (2): A : ob; B : ob; // mor (3): f : mor; #3 : mor; #4 : mor; // src: f src = A; #3 src = A; #4 src = B; // tgt: f tgt = B; #3 tgt = A; #4 tgt = B; // comp (4 tuples): [f: f, g: #4, h: f] comp; [f: #3, g: f, h: f] comp; [f: #3, g: #3, h: #3] comp; [f: #4, g: #4, h: #4] comp; // id (2 tuples): [a: A, f: #3] id; [a: B, f: #4] id; } ``` The `Arrow` instance declares only objects A, B and one morphism f : A → B. The chase derives identity morphisms (#3 = idA, #4 = idB) and all compositions, while **equality saturation** collapses infinite self-compositions via unit laws: - `[f: #3, g: f, h: f]` means idA;f = f (left unit) - `[f: f, g: #4, h: f]` means f;idB = f (right unit) - `[f: #3, g: #3, h: #3]` means idA;idA = idA (collapsed by unit law) ## Features - **Theories**: Define sorts (types), functions, relations, and axioms - **Instances**: Create concrete models of theories - **Parameterized Theories**: Theories can depend on instances of other theories - **Nested Instances**: Inline instance definitions within instances - **Relations**: Binary and n-ary predicates with product domains - **Axioms**: Geometric sequents, automatically checked with tensor algebra - **Chase Algorithm**: Automatic inference of derived facts - **Interactive REPL**: Explore and modify instances dynamically - **Version Control**: Commit and track changes to instances --- ## Showcase: Petri Net Reachability as Dependent Types This showcase demonstrates geolog's core capabilities through a non-trivial domain: encoding Petri net reachability as dependent types. A solution to a reachability problem is NOT a yes/no boolean but a **constructive witness**: a diagrammatic proof that tokens can flow from initial to target markings via a sequence of transition firings. **Key concepts demonstrated:** - Parameterized theories (`Marking` depends on `PetriNet` instance) - Nested instance types (`ReachabilityProblem` contains `Marking` instances) - Sort-parameterized theories (`Iso` takes two sorts as parameters) - Cross-instance references (solution's trace elements reference problem's tokens) > **Note**: This showcase is tested by `cargo test test_petri_net_showcase` and > matches `examples/geolog/petri_net_showcase.geolog` exactly. ### The Type-Theoretic Encoding ```geolog // ============================================================ // THEORY: PetriNet - Places, transitions, and arcs // ============================================================ theory PetriNet { P : Sort; // Places T : Sort; // Transitions in : Sort; // Input arcs (place -> transition) out : Sort; // Output arcs (transition -> place) in/src : in -> P; // Input arc source place in/tgt : in -> T; // Input arc target transition out/src : out -> T; // Output arc source transition out/tgt : out -> P; // Output arc target place } // ============================================================ // THEORY: Marking (parameterized by N : PetriNet) // A marking assigns tokens to places // ============================================================ theory (N : PetriNet instance) Marking { token : Sort; token/of : token -> N/P; } // ============================================================ // THEORY: ReachabilityProblem (parameterized by N : PetriNet) // Initial and target markings as nested instances // ============================================================ theory (N : PetriNet instance) ReachabilityProblem { initial_marking : N Marking instance; target_marking : N Marking instance; } // ============================================================ // THEORY: Trace (parameterized by N : PetriNet) // A trace records transition firings and token flow via wires // ============================================================ theory (N : PetriNet instance) Trace { F : Sort; // Firings F/of : F -> N/T; // Which transition each firing corresponds to // Wires connect output arcs of firings to input arcs of other firings W : Sort; W/src_firing : W -> F; W/src_arc : W -> N/out; W/tgt_firing : W -> F; W/tgt_arc : W -> N/in; // Wire coherence axioms (source/target arcs must match firing transitions) ax/wire_src_coherent : forall w : W. |- w W/src_arc N/out/src = w W/src_firing F/of; ax/wire_tgt_coherent : forall w : W. |- w W/tgt_arc N/in/tgt = w W/tgt_firing F/of; ax/wire_place_coherent : forall w : W. |- w W/src_arc N/out/tgt = w W/tgt_arc N/in/src; // Terminals connect initial/target markings to firings input_terminal : Sort; output_terminal : Sort; input_terminal/of : input_terminal -> N/P; output_terminal/of : output_terminal -> N/P; input_terminal/tgt_firing : input_terminal -> F; input_terminal/tgt_arc : input_terminal -> N/in; output_terminal/src_firing : output_terminal -> F; output_terminal/src_arc : output_terminal -> N/out; // Terminal coherence axioms ax/input_terminal_coherent : forall i : input_terminal. |- i input_terminal/tgt_arc N/in/tgt = i input_terminal/tgt_firing F/of; ax/output_terminal_coherent : forall o : output_terminal. |- o output_terminal/src_arc N/out/src = o output_terminal/src_firing F/of; // Terminal place coherence ax/input_terminal_place : forall i : input_terminal. |- i input_terminal/of = i input_terminal/tgt_arc N/in/src; ax/output_terminal_place : forall o : output_terminal. |- o output_terminal/of = o output_terminal/src_arc N/out/tgt; // COMPLETENESS: Every arc of every firing must be accounted for. // Input completeness: every input arc must be fed by a wire or input terminal ax/input_complete : forall f : F, arc : N/in. arc N/in/tgt = f F/of |- (exists w : W. w W/tgt_firing = f, w W/tgt_arc = arc) \/ (exists i : input_terminal. i input_terminal/tgt_firing = f, i input_terminal/tgt_arc = arc); // Output completeness: every output arc must be captured by a wire or output terminal ax/output_complete : forall f : F, arc : N/out. arc N/out/src = f F/of |- (exists w : W. w W/src_firing = f, w W/src_arc = arc) \/ (exists o : output_terminal. o output_terminal/src_firing = f, o output_terminal/src_arc = arc); } // ============================================================ // THEORY: Iso (parameterized by two sorts) // Isomorphism (bijection) between two sorts // ============================================================ theory (X : Sort) (Y : Sort) Iso { fwd : X -> Y; bwd : Y -> X; // Roundtrip axioms ensure this is a true bijection fb : forall x : X. |- x fwd bwd = x; bf : forall y : Y. |- y bwd fwd = y; } // ============================================================ // THEORY: Solution (parameterized by N and RP) // A constructive witness that target is reachable from initial // ============================================================ theory (N : PetriNet instance) (RP : N ReachabilityProblem instance) Solution { trace : N Trace instance; // Bijection: input terminals <-> initial marking tokens initial_iso : (trace/input_terminal) (RP/initial_marking/token) Iso instance; // Bijection: output terminals <-> target marking tokens target_iso : (trace/output_terminal) (RP/target_marking/token) Iso instance; } ``` ### Problem 0: Can we reach B from A with one token? ```geolog // ============================================================ // The Petri Net: // +---[ba]----+ // v | // (A) --[ab]->(B) --+ // | | // +----[abc]-------+--> (C) // ============================================================ instance ExampleNet : PetriNet = { A : P; B : P; C : P; ab : T; ba : T; abc : T; // A -> B (via ab) ab_in : in; ab_in in/src = A; ab_in in/tgt = ab; ab_out : out; ab_out out/src = ab; ab_out out/tgt = B; // B -> A (via ba) ba_in : in; ba_in in/src = B; ba_in in/tgt = ba; ba_out : out; ba_out out/src = ba; ba_out out/tgt = A; // A + B -> C (via abc) - note: two input arcs! abc_in1 : in; abc_in1 in/src = A; abc_in1 in/tgt = abc; abc_in2 : in; abc_in2 in/src = B; abc_in2 in/tgt = abc; abc_out : out; abc_out out/src = abc; abc_out out/tgt = C; } // Initial: 1 token in A, Target: 1 token in B instance problem0 : ExampleNet ReachabilityProblem = { initial_marking = { tok : token; tok token/of = ExampleNet/A; }; target_marking = { tok : token; tok token/of = ExampleNet/B; }; } // ============================================================ // SOLUTION 0: Yes! Fire transition 'ab' once. // ============================================================ instance solution0 : ExampleNet problem0 Solution = { trace = { f1 : F; f1 F/of = ExampleNet/ab; // Input terminal feeds A-token into f1's ab_in arc it : input_terminal; it input_terminal/of = ExampleNet/A; it input_terminal/tgt_firing = f1; it input_terminal/tgt_arc = ExampleNet/ab_in; // Output terminal captures f1's B-token via ab_out arc ot : output_terminal; ot output_terminal/of = ExampleNet/B; ot output_terminal/src_firing = f1; ot output_terminal/src_arc = ExampleNet/ab_out; }; initial_iso = { trace/it fwd = problem0/initial_marking/tok; problem0/initial_marking/tok bwd = trace/it; }; target_iso = { trace/ot fwd = problem0/target_marking/tok; problem0/target_marking/tok bwd = trace/ot; }; } ``` ### Problem 2: Can we reach C from two A-tokens? This is a more interesting case: the only path to C is via `abc`, which requires tokens in BOTH A and B simultaneously. Starting with 2 tokens in A, we must first move one to B, then fire `abc`. ```geolog // Initial: 2 tokens in A, Target: 1 token in C instance problem2 : ExampleNet ReachabilityProblem = { initial_marking = { t1 : token; t1 token/of = ExampleNet/A; t2 : token; t2 token/of = ExampleNet/A; }; target_marking = { t : token; t token/of = ExampleNet/C; }; } // ============================================================ // SOLUTION 2: Fire 'ab' then 'abc'. // // Token flow diagram: // [it1]--A-->[f1: ab]--B--wire-->[f2: abc]--C-->[ot] // [it2]--A-----------------^ // // Step 1: Fire 'ab' to move one token A -> B // Step 2: Fire 'abc' consuming one A-token and one B-token // ============================================================ instance solution2 : ExampleNet problem2 Solution = { trace = { // Two firings f1 : F; f1 F/of = ExampleNet/ab; // First: A -> B f2 : F; f2 F/of = ExampleNet/abc; // Second: A + B -> C // Wire connecting f1's B-output to f2's B-input w1 : W; w1 W/src_firing = f1; w1 W/src_arc = ExampleNet/ab_out; w1 W/tgt_firing = f2; w1 W/tgt_arc = ExampleNet/abc_in2; // Input terminal 1: feeds first A-token into f1 it1 : input_terminal; it1 input_terminal/of = ExampleNet/A; it1 input_terminal/tgt_firing = f1; it1 input_terminal/tgt_arc = ExampleNet/ab_in; // Input terminal 2: feeds second A-token into f2 it2 : input_terminal; it2 input_terminal/of = ExampleNet/A; it2 input_terminal/tgt_firing = f2; it2 input_terminal/tgt_arc = ExampleNet/abc_in1; // Output terminal: captures f2's C-token output ot : output_terminal; ot output_terminal/of = ExampleNet/C; ot output_terminal/src_firing = f2; ot output_terminal/src_arc = ExampleNet/abc_out; }; // Bijection: 2 input terminals <-> 2 initial tokens initial_iso = { trace/it1 fwd = problem2/initial_marking/t1; trace/it2 fwd = problem2/initial_marking/t2; problem2/initial_marking/t1 bwd = trace/it1; problem2/initial_marking/t2 bwd = trace/it2; }; // Bijection: 1 output terminal <-> 1 target token target_iso = { trace/ot fwd = problem2/target_marking/t; problem2/target_marking/t bwd = trace/ot; }; } ``` Each `Solution` instance is a **constructive diagrammatic proof**: - The trace contains firing(s) of specific transitions - Input terminals witness that initial tokens feed into firings - Output terminals witness that firings produce target tokens - The isomorphisms prove the token counts match exactly --- ## Table of Contents 1. [Basic Concepts](#basic-concepts) 2. [Theory Definitions](#theory-definitions) 3. [Instance Definitions](#instance-definitions) 4. [Relations and Axioms](#relations-and-axioms) 5. [The Chase Algorithm](#the-chase-algorithm) 6. [REPL Commands](#repl-commands) 7. [Complete Examples](#complete-examples) --- ## Basic Concepts Geolog is based on **geometric logic**, a fragment of first-order logic that: - Allows existential quantification in conclusions - Allows disjunctions in conclusions - Is preserved by geometric morphisms (structure-preserving maps) A **theory** defines: - **Sorts**: Types of elements - **Function symbols**: Function-typed variables with domain and codomain derived from sorts - **Relation symbols**: Predicate-typed variables with domain derived from sorts, and codomain `-> Prop` - **Axioms**: Geometric sequents (first universal quantifiers, then an implication between two propositions which are then purely positive) An **instance** is a concrete finite model, which means it assigns to each sort a finite set, to each function a finite function, and to each relation a Boolean-valued tensor, such that all axioms evaluate to true. --- ## Theory Definitions ### Simple Theory with Sorts and Functions ```geolog // Directed Graph: vertices and edges with source/target functions theory Graph { V : Sort; // Vertices E : Sort; // Edges src : E -> V; // Source of an edge tgt : E -> V; // Target of an edge } ``` ### Theory with Product Domain Functions ```geolog // Monoid: a set with an associative binary operation theory Monoid { M : Sort; // Binary operation: M × M → M mul : [x: M, y: M] -> M; // Identity element id : M -> M; // Associativity: (x * y) * z = x * (y * z) ax/assoc : forall x : M, y : M, z : M. |- [x: [x: x, y: y] mul, y: z] mul = [x: x, y: [x: y, y: z] mul] mul; } ``` ### REPL Session: Defining a Theory Inline ``` geolog> theory Counter { ...... C : Sort; ...... next : C -> C; ...... } Defined theory Counter (1 sorts, 1 functions) geolog> :inspect Counter theory Counter { C : Sort; next : C -> C; } ``` --- ## Instance Definitions ### Basic Instance ```geolog // A simple triangle graph: A → B → C → A instance Triangle : Graph = { // Vertices A : V; B : V; C : V; // Edges ab : E; bc : E; ca : E; // Edge endpoints (function definitions) ab src = A; ab tgt = B; bc src = B; bc tgt = C; ca src = C; ca tgt = A; } ``` ### Instance with Product Domain Functions ```geolog // Boolean "And" monoid: {T, F} with T as identity instance BoolAnd : Monoid = { T : M; F : M; // Identity: T is the identity element T id = T; F id = T; // Multiplication table for "and": [x: T, y: T] mul = T; [x: T, y: F] mul = F; [x: F, y: T] mul = F; [x: F, y: F] mul = F; } ``` ### REPL Session: Loading and Inspecting ``` geolog> :source examples/geolog/graph.geolog Loading examples/geolog/graph.geolog... Defined theory Graph (2 sorts, 2 functions) geolog> :list Theories: Graph (2 sorts, 2 functions) Instances: Diamond : Graph (8 elements) Arrow : Graph (3 elements) Loop : Graph (2 elements) Triangle : Graph (6 elements) geolog> :inspect Triangle instance Triangle : Graph = { // V (3): A : V; B : V; C : V; // E (3): ab : E; bc : E; ca : E; // src: ab src = A; bc src = B; ca src = C; // tgt: ab tgt = B; bc tgt = C; ca tgt = A; } geolog> :query Triangle V Elements of V in Triangle: A B C ``` --- ## Relations and Axioms Relations are predicates on sorts, declared with `-> Prop`. ### Unary Relations ```geolog theory TodoList { Item : Sort; // Unary relations use simple arrow syntax completed : Item -> Prop; high_priority : Item -> Prop; blocked : Item -> Prop; } ``` ### Binary Relations ```geolog theory Preorder { X : Sort; // Binary relation: x ≤ y (field names document the relation) leq : [lo: X, hi: X] -> Prop; // Reflexivity axiom: x ≤ x ax/refl : forall x : X. |- [lo: x, hi: x] leq; // Transitivity axiom: x ≤ y ∧ y ≤ z → x ≤ z ax/trans : forall x : X, y : X, z : X. [lo: x, hi: y] leq, [lo: y, hi: z] leq |- [lo: x, hi: z] leq; } ``` ### Asserting Relation Tuples in Instances ```geolog instance SampleTodos : TodoList = { buy_groceries : Item; cook_dinner : Item; do_laundry : Item; clean_house : Item; // Assert unary relation: buy_groceries is completed buy_groceries completed; // Assert unary relation: cook_dinner is high priority cook_dinner high_priority; // Binary relation using mixed positional/named syntax: // First positional arg maps to 'item' field, named arg for 'on' [cook_dinner, on: buy_groceries] depends; } ``` ### REPL Session: Asserting Relations Dynamically ``` geolog> :source examples/geolog/todo_list.geolog Loading examples/geolog/todo_list.geolog... Defined theory TodoList (1 sorts, 4 relations) geolog> :inspect SampleTodos instance SampleTodos : TodoList = { // Item (4): buy_groceries : Item; cook_dinner : Item; do_laundry : Item; clean_house : Item; // completed (1 tuples): [buy_groceries] completed; // high_priority (1 tuples): [cook_dinner] high_priority; // depends (1 tuples): [cook_dinner, buy_groceries] depends; } geolog> :assert SampleTodos completed cook_dinner Asserted completed(cook_dinner) in instance 'SampleTodos' geolog> :inspect SampleTodos instance SampleTodos : TodoList = { // Item (4): buy_groceries : Item; cook_dinner : Item; do_laundry : Item; clean_house : Item; // completed (2 tuples): [buy_groceries] completed; [cook_dinner] completed; // high_priority (1 tuples): [cook_dinner] high_priority; // depends (1 tuples): [cook_dinner, buy_groceries] depends; } ``` --- ## The Chase Algorithm The **chase algorithm** computes the closure of an instance under the theory's axioms. It derives all facts that logically follow from the base facts and axioms. ### Transitive Closure Example ```geolog // Graph with reachability (transitive closure) theory Graph { V : Sort; // Direct edges Edge : [src: V, tgt: V] -> Prop; // Reachability (transitive closure of Edge) Path : [src: V, tgt: V] -> Prop; // Base case: every edge is a path ax/base : forall x, y : V. [src: x, tgt: y] Edge |- [src: x, tgt: y] Path; // Inductive case: paths compose ax/trans : forall x, y, z : V. [src: x, tgt: y] Path, [src: y, tgt: z] Path |- [src: x, tgt: z] Path; } // A linear chain: a -> b -> c -> d // Using `= chase { ... }` to automatically apply axioms during elaboration. instance Chain : Graph = chase { a : V; b : V; c : V; d : V; // Initial edges (chase derives Path tuples) [src: a, tgt: b] Edge; [src: b, tgt: c] Edge; [src: c, tgt: d] Edge; } ``` ### REPL Session: Running the Chase When using `= chase { ... }` syntax, the chase runs automatically during elaboration: ``` geolog> :source examples/geolog/transitive_closure.geolog Loading examples/geolog/transitive_closure.geolog... Defined theory Graph (1 sorts, 2 relations) Defined instance Chain : Graph (4 elements) [chase: 6 Path tuples derived] geolog> :inspect Chain instance Chain : Graph = { // V (4): a : V; b : V; c : V; d : V; // Edge (3 tuples): [a, b] Edge; [b, c] Edge; [c, d] Edge; // Path (6 tuples): [a, b] Path; [b, c] Path; [c, d] Path; [a, c] Path; // Derived: a->b + b->c [b, d] Path; // Derived: b->c + c->d [a, d] Path; // Derived: a->c + c->d (or a->b + b->d) } ``` You can also run chase manually with `:chase` on non-chase instances: ``` geolog> :chase MyInstance Running chase on instance 'MyInstance' (theory 'Graph')... ✓ Chase completed in 3 iterations (0.15ms) ``` The chase derived: - **3 base paths** from the Edge → Path axiom - **2 one-step transitive paths**: (a,c) and (b,d) - **1 two-step transitive path**: (a,d) --- ## REPL Commands ### General Commands | Command | Description | |---------|-------------| | `:help [topic]` | Show help (topics: syntax, examples) | | `:quit` | Exit the REPL | | `:list [target]` | List theories/instances | | `:inspect ` | Show details of a theory or instance | | `:source ` | Load and execute a .geolog file | | `:clear` | Clear the screen | | `:reset` | Reset all state | ### Instance Mutation | Command | Description | |---------|-------------| | `:add ` | Add element to instance | | `:assert [args]` | Assert relation tuple | | `:retract ` | Retract element | ### Query Commands | Command | Description | |---------|-------------| | `:query ` | List all elements of a sort | | `:explain ` | Show query execution plan | | `:compile ` | Show RelAlgIR compilation | | `:chase [max_iter]` | Run chase algorithm | ### Version Control | Command | Description | |---------|-------------| | `:commit [msg]` | Commit current changes | | `:history` | Show commit history | ### Solver | Command | Description | |---------|-------------| | `:solve [budget_ms]` | Find model of theory | | `:extend [budget_ms]` | Extend instance to theory | ### REPL Session: Query Explanation ``` geolog> :source examples/geolog/graph.geolog Loading examples/geolog/graph.geolog... Defined theory Graph (2 sorts, 2 functions) geolog> :explain Triangle V Query plan for ':query Triangle V': Scan(sort=0) Sort: V (index 0) Instance: Triangle (theory: Graph) geolog> :explain Triangle E Query plan for ':query Triangle E': Scan(sort=1) Sort: E (index 1) Instance: Triangle (theory: Graph) ``` --- ## Complete Examples ### Example 1: Directed Graphs **File: `examples/geolog/graph.geolog`** ```geolog // Directed Graph: vertices and edges with source/target functions theory Graph { V : Sort; // Vertices E : Sort; // Edges src : E -> V; // Source of an edge tgt : E -> V; // Target of an edge } // A simple triangle graph: A → B → C → A instance Triangle : Graph = { A : V; B : V; C : V; ab : E; bc : E; ca : E; ab src = A; ab tgt = B; bc src = B; bc tgt = C; ca src = C; ca tgt = A; } // A self-loop: one vertex with an edge to itself instance Loop : Graph = { v : V; e : E; e src = v; e tgt = v; } // Diamond shape with two paths from top to bottom instance Diamond : Graph = { top : V; left : V; right : V; bottom : V; top_left : E; top_right : E; left_bottom : E; right_bottom : E; top_left src = top; top_left tgt = left; top_right src = top; top_right tgt = right; left_bottom src = left; left_bottom tgt = bottom; right_bottom src = right; right_bottom tgt = bottom; } ``` --- ### Example 2: Algebraic Structures (Monoids) **File: `examples/geolog/monoid.geolog`** ```geolog // Monoid: a set with an associative binary operation and identity theory Monoid { M : Sort; // Binary operation: M × M → M mul : [x: M, y: M] -> M; // Identity element selector id : M -> M; // Left identity: id(x) * y = y ax/left_id : forall x : M, y : M. |- [x: x id, y: y] mul = y; // Right identity: x * id(y) = x ax/right_id : forall x : M, y : M. |- [x: x, y: y id] mul = x; // Associativity: (x * y) * z = x * (y * z) ax/assoc : forall x : M, y : M, z : M. |- [x: [x: x, y: y] mul, y: z] mul = [x: x, y: [x: y, y: z] mul] mul; } // Trivial monoid: single element instance Trivial : Monoid = { e : M; [x: e, y: e] mul = e; e id = e; } // Boolean "And" monoid instance BoolAnd : Monoid = { T : M; F : M; T id = T; F id = T; [x: T, y: T] mul = T; [x: T, y: F] mul = F; [x: F, y: T] mul = F; [x: F, y: F] mul = F; } // Boolean "Or" monoid instance BoolOr : Monoid = { T : M; F : M; T id = F; F id = F; [x: T, y: T] mul = T; [x: T, y: F] mul = T; [x: F, y: T] mul = T; [x: F, y: F] mul = F; } ``` --- ### Example 3: Preorders with Chase **File: `examples/geolog/preorder.geolog`** ```geolog // Preorder: reflexive and transitive relation theory Preorder { X : Sort; // The ordering relation: x ≤ y leq : [x: X, y: X] -> Prop; // Reflexivity: x ≤ x ax/refl : forall x : X. |- [x: x, y: x] leq; // Transitivity: x ≤ y ∧ y ≤ z → x ≤ z ax/trans : forall x : X, y : X, z : X. [x: x, y: y] leq, [x: y, y: z] leq |- [x: x, y: z] leq; } // Discrete preorder: only reflexive pairs // Uses `chase` to automatically derive reflexive pairs from ax/refl. instance Discrete3 : Preorder = chase { a : X; b : X; c : X; } // A total order on 3 elements: bot ≤ mid ≤ top instance Chain3 : Preorder = chase { bot : X; mid : X; top : X; [x: bot, y: mid] leq; [x: mid, y: top] leq; // Chase derives: (bot,bot), (mid,mid), (top,top) + (bot,top) } ``` **REPL Session:** ``` geolog> :source examples/geolog/preorder.geolog Defined theory Preorder (1 sorts, 1 relations) Defined instance Discrete3 : Preorder (3 elements) [chase: 3 leq tuples derived] Defined instance Chain3 : Preorder (3 elements) [chase: 6 leq tuples derived] geolog> :inspect Discrete3 leq: 3 tuple(s) // (a,a), (b,b), (c,c) - reflexivity only geolog> :inspect Chain3 leq: 6 tuple(s) // reflexive pairs + given + transitive (bot,top) ``` --- ### Example 4: Task Management **File: `examples/geolog/todo_list.geolog`** ```geolog // TodoList: relational model for task tracking theory TodoList { Item : Sort; // Status relations (unary, simple arrow syntax) completed : Item -> Prop; high_priority : Item -> Prop; blocked : Item -> Prop; // Dependencies (binary, with named fields) depends : [item: Item, on: Item] -> Prop; // Axiom: blocked items depend on incomplete items ax/dep_blocked : forall x : Item, y : Item. [item: x, on: y] depends |- x blocked \/ y completed; } instance SampleTodos : TodoList = { buy_groceries : Item; cook_dinner : Item; do_laundry : Item; clean_house : Item; // Unary relations: simple syntax buy_groceries completed; cook_dinner high_priority; // Binary relation: mixed positional/named syntax // First positional arg -> 'item', named arg for 'on' [cook_dinner, on: buy_groceries] depends; } ``` --- ### Example 5: Transitive Closure (Chase Demo) **File: `examples/geolog/transitive_closure.geolog`** ```geolog // Transitive Closure - demonstrates the chase algorithm theory Graph { V : Sort; Edge : [src: V, tgt: V] -> Prop; Path : [src: V, tgt: V] -> Prop; // Base: edges are paths ax/base : forall x, y : V. [src: x, tgt: y] Edge |- [src: x, tgt: y] Path; // Transitivity: paths compose ax/trans : forall x, y, z : V. [src: x, tgt: y] Path, [src: y, tgt: z] Path |- [src: x, tgt: z] Path; } // Linear chain: a -> b -> c -> d (chase runs automatically) instance Chain : Graph = chase { a : V; b : V; c : V; d : V; [src: a, tgt: b] Edge; [src: b, tgt: c] Edge; [src: c, tgt: d] Edge; } // Diamond: two paths from top to bottom instance Diamond : Graph = chase { top : V; left : V; right : V; bottom : V; [src: top, tgt: left] Edge; [src: top, tgt: right] Edge; [src: left, tgt: bottom] Edge; [src: right, tgt: bottom] Edge; } // Cycle: x -> y -> z -> x (chase computes all 9 pairs!) instance Cycle : Graph = chase { x : V; y : V; z : V; [src: x, tgt: y] Edge; [src: y, tgt: z] Edge; [src: z, tgt: x] Edge; } ``` **REPL Session** (chase runs during `:source`): ``` geolog> :source examples/geolog/transitive_closure.geolog Defined theory Graph (1 sorts, 2 relations) Defined instance Chain : Graph (4 elements) [chase: 6 Path tuples] Defined instance Diamond : Graph (4 elements) [chase: 5 Path tuples] Defined instance Cycle : Graph (3 elements) [chase: 9 Path tuples] ``` --- ### Example 6: Inline Definitions You can define theories and instances directly in the REPL: ``` geolog> theory Counter { ...... C : Sort; ...... next : C -> C; ...... } Defined theory Counter (1 sorts, 1 functions) geolog> instance Mod3 : Counter = { ...... zero : C; ...... one : C; ...... two : C; ...... zero next = one; ...... one next = two; ...... two next = zero; ...... } Defined instance Mod3 : Counter (3 elements) geolog> :inspect Mod3 instance Mod3 : Counter = { // C (3): zero : C; one : C; two : C; // next: zero next = one; one next = two; two next = zero; } ``` --- ## Syntax Reference ### Sorts ``` identifier : Sort; ``` ### Functions ``` // Unary function name : Domain -> Codomain; // Binary function (product domain) name : [field1: Sort1, field2: Sort2] -> Codomain; ``` ### Relations ``` // Unary relation name : [field: Sort] -> Prop; // Binary relation name : [x: Sort1, y: Sort2] -> Prop; ``` ### Axioms ``` // No premises (fact) name : forall vars. |- conclusion; // With premises name : forall vars. premise1, premise2 |- conclusion; // With disjunction in conclusion name : forall vars. premise |- conclusion1 \/ conclusion2; ``` ### Instance Elements ``` elem_name : Sort; ``` ### Function Values ``` // Unary elem func = value; // Product domain [field1: val1, field2: val2] func = value; ``` ### Relation Assertions ``` // Unary relation elem relation; // Binary relation [field1: val1, field2: val2] relation; ``` --- ## Architecture > TODO: greatly expand this section Geolog is built with several key components: - **Parser**: Converts `.geolog` source to AST - **Elaborator**: Type-checks and converts AST to core representations - **Structure**: In-memory model representation with carriers and functions - **Chase Engine**: Fixpoint computation for derived relations - **Query Engine**: Relational algebra for querying instances - **Store**: Persistent, append-only storage with version control --- ## License MIT License - see LICENSE file for details. --- ## Contributing Contributions welcome! See CLAUDE.md for development guidelines and the `loose_thoughts/` directory for design discussions.