Skip to content

Pipe-style function call#

Code Example

Runnable Example in Jac and JacLib

"""Pipe-style function call: Pipe-style function invocation."""

# ===== Part 1: Node and Walker Setup =====
node Task {
    has name: str;
    has priority: int = 0;
}

walker SimpleWalker {
    has visited_names: list = [];

    can start with `root entry {
        print(f"SimpleWalker: Starting at root");
        visit [-->];
    }

    can process with Task entry {
        self.visited_names.append(here.name);
        print(f"  Processing: {here.name} (priority {here.priority})");
        visit [-->];
    }
}

# ===== Part 2: Basic spawn Keyword =====
walker BasicSpawn {
    can start with `root entry {
        print("BasicSpawn: started at root");
        visit [-->];
    }

    can handle_task with Task entry {
        print(f"  BasicSpawn at: {here.name}");
        visit [-->];
    }
}

# ===== Part 3: Depth-First Spatial Call (:>) =====
walker DepthFirst {
    has depth: int = 0;

    can start with `root entry {
        print("DepthFirst: root");
        visit [-->];
    }

    can process with Task entry {
        self.depth += 1;
        print(f"  Depth-first [{self.depth}]: {here.name}");
        visit [-->];
    }
}

# ===== Part 4: Breadth-First Spatial Call (|>) =====
walker BreadthFirst {
    has order: list = [];

    can start with `root entry {
        print("BreadthFirst: root");
        visit [-->];
    }

    can process with Task entry {
        self.order.append(here.name);
        print(f"  Breadth-first: {here.name}");
        visit [-->];
    }
}

# ===== Part 5: Walker Return Values =====
walker DataCollector {
    has collected: list = [];
    has sum: int = 0;

    can start with `root entry {
        visit [-->];
    }

    can collect with Task entry {
        self.collected.append(here.name);
        self.sum += here.priority;
        visit [-->];
    }
}

# ===== Part 6: Walker Spawned from Nodes =====
walker NodeSpawner {
    can start with `root entry {
        visit [-->];
    }

    can process with Task entry {
        print(f"  At {here.name}");
        if here.name == "Task2" {
            print("    Spawning SubWalker from Task2");
            # Spawn another walker from current node
            here spawn SubWalker();
        }
        visit [-->];
    }
}

walker SubWalker {
    can start with Task entry {
        print(f"    SubWalker started at: {here.name}");
        visit [-->];
    }

    can handle with Task entry {
        print(f"      SubWalker processing: {here.name}");
        visit [-->];
    }
}

# ===== Part 7: Walker Construction and Spawn Patterns =====
walker ConstructedWalker {
    has label: str;
    has max_visits: int = 5;
    has visits: int = 0;

    can start with `root entry {
        print(f"ConstructedWalker '{self.label}' starting");
        visit [-->];
    }

    can process with Task entry {
        self.visits += 1;
        print(f"  [{self.label}] Visit {self.visits}: {here.name}");

        if self.visits >= self.max_visits {
            print(f"  [{self.label}] Max visits reached");
            disengage;
        }

        visit [-->];
    }
}

# ===== Part 8: Multiple Walkers on Same Graph =====
walker Counter {
    has total: int = 0;

    can start with `root entry {
        visit [-->];
    }

    can count_task with Task entry {
        self.total += 1;
        visit [-->];
    }
}

walker Analyzer {
    has high_priority: int = 0;
    has low_priority: int = 0;

    can start with `root entry {
        visit [-->];
    }

    can analyze with Task entry {
        if here.priority > 5 {
            self.high_priority += 1;
        } else {
            self.low_priority += 1;
        }
        visit [-->];
    }
}

# ===== Part 9: Walker Spawn Syntax Variations =====
walker SyntaxDemo {
    can start with `root entry {
        print("SyntaxDemo: Demonstrating all spawn syntaxes");
    }
}

# ===== Execution and Tests =====
with entry {
    print("=== 1. Building Test Graph ===");
    # Build tree structure:
    #     root
    #      |
    #    Task1 (p=10)
    #    /   \
    # Task2  Task3 (p=3)
    #  (p=5)   |
    #          Task4 (p=8)

    task1 = Task(name="Task1", priority=10);
    task2 = Task(name="Task2", priority=5);
    task3 = Task(name="Task3", priority=3);
    task4 = Task(name="Task4", priority=8);

    root ++> task1;
    task1 ++> task2;
    task1 ++> task3;
    task3 ++> task4;

    print("Graph: root -> Task1 -> Task2");
    print("                 \\-> Task3 -> Task4\n");

    print("=== 2. Basic spawn Keyword ===");
    # Most common form: node spawn Walker()
    root spawn BasicSpawn();

    print("\n=== 3. Depth-First Spatial Call (:>) ===");
    # Depth-first traversal: goes deep before wide
    # Expected: Task1, Task2, Task3, Task4
    root spawn :> DepthFirst;

    print("\n=== 4. Breadth-First Spatial Call (|>) ===");
    # Breadth-first traversal: goes level by level
    # Expected: Task1, Task2, Task3, Task4
    walker_bf = root spawn |> BreadthFirst;
    print(f"  Order visited: {walker_bf.order}");

    print("\n=== 5. Walker Return Values ===");
    # Walker instance is returned, can access state
    collector = root spawn DataCollector();
    print(f"  Collected: {collector.collected}");
    print(f"  Sum of priorities: {collector.sum}");

    print("\n=== 6. Walker Spawned from Nodes ===");
    # Walkers can spawn other walkers from any node
    root spawn NodeSpawner();

    print("\n=== 7. Walker Construction with Arguments ===");
    # Create walker with initial state
    w1 = root spawn ConstructedWalker(label="Alpha", max_visits=2);
    print(f"  Walker visited {w1.visits} tasks");

    print("\n=== 8. Multiple Walkers on Same Graph ===");
    # Different walkers can analyze same graph structure
    counter = root spawn Counter();
    analyzer = root spawn Analyzer();

    print(f"  Counter: {counter.total} tasks");
    print(f"  Analyzer: {analyzer.high_priority} high, {analyzer.low_priority} low priority");

    print("\n=== 9. Spawn Syntax Variations ===");
    # Variation 1: Basic spawn
    root spawn SyntaxDemo();

    # Variation 2: Depth-first spawn
    root spawn :> SyntaxDemo;

    # Variation 3: Breadth-first spawn
    root spawn |> SyntaxDemo;

    # Variation 4: Walker-first syntax (less common)
    SyntaxDemo() spawn root;

    # Variation 5: With constructed walker
    demo = SyntaxDemo();
    root spawn demo;

    print("\n=== 10. Traversal Strategy Comparison ===");
    # Build a deeper tree to see difference
    a = Task(name="A", priority=1);
    b = Task(name="B", priority=2);
    c = Task(name="C", priority=3);
    d = Task(name="D", priority=4);
    e = Task(name="E", priority=5);

    root ++> a;
    a ++> b;
    a ++> c;
    b ++> d;
    b ++> e;

    print("New graph: root -> A -> B -> D");
    print("                    \\   \\-> E");
    print("                     \\-> C\n");

    print("  Depth-first (:>):");
    df = root spawn :> SimpleWalker;
    print(f"    Visited: {df.visited_names}");

    print("\n  Breadth-first (|>):");
    bf = root spawn |> SimpleWalker;
    print(f"    Visited: {bf.visited_names}");

    print("\n✓ Object spatial calls demonstrated!");
}
"""Pipe-style function call: Pipe-style function invocation."""

# ===== Part 1: Node and Walker Setup =====
node Task {
    has name: str;
    has priority: int = 0;
}

walker SimpleWalker {
    has visited_names: list = [];

    can start with `root entry {
        print(f"SimpleWalker: Starting at root");
        visit [-->];
    }

    can process with Task entry {
        self.visited_names.append(here.name);
        print(f"  Processing: {here.name} (priority {here.priority})");
        visit [-->];
    }
}

# ===== Part 2: Basic spawn Keyword =====
walker BasicSpawn {
    can start with `root entry {
        print("BasicSpawn: started at root");
        visit [-->];
    }

    can handle_task with Task entry {
        print(f"  BasicSpawn at: {here.name}");
        visit [-->];
    }
}

# ===== Part 3: Depth-First Spatial Call (:>) =====
walker DepthFirst {
    has depth: int = 0;

    can start with `root entry {
        print("DepthFirst: root");
        visit [-->];
    }

    can process with Task entry {
        self.depth += 1;
        print(f"  Depth-first [{self.depth}]: {here.name}");
        visit [-->];
    }
}

# ===== Part 4: Breadth-First Spatial Call (|>) =====
walker BreadthFirst {
    has order: list = [];

    can start with `root entry {
        print("BreadthFirst: root");
        visit [-->];
    }

    can process with Task entry {
        self.order.append(here.name);
        print(f"  Breadth-first: {here.name}");
        visit [-->];
    }
}

# ===== Part 5: Walker Return Values =====
walker DataCollector {
    has collected: list = [];
    has sum: int = 0;

    can start with `root entry {
        visit [-->];
    }

    can collect with Task entry {
        self.collected.append(here.name);
        self.sum += here.priority;
        visit [-->];
    }
}

# ===== Part 6: Walker Spawned from Nodes =====
walker NodeSpawner {
    can start with `root entry {
        visit [-->];
    }

    can process with Task entry {
        print(f"  At {here.name}");
        if here.name == "Task2" {
            print("    Spawning SubWalker from Task2");
            # Spawn another walker from current node
            here spawn SubWalker();
        }
        visit [-->];
    }
}

walker SubWalker {
    can start with Task entry {
        print(f"    SubWalker started at: {here.name}");
        visit [-->];
    }

    can handle with Task entry {
        print(f"      SubWalker processing: {here.name}");
        visit [-->];
    }
}

# ===== Part 7: Walker Construction and Spawn Patterns =====
walker ConstructedWalker {
    has label: str;
    has max_visits: int = 5;
    has visits: int = 0;

    can start with `root entry {
        print(f"ConstructedWalker '{self.label}' starting");
        visit [-->];
    }

    can process with Task entry {
        self.visits += 1;
        print(f"  [{self.label}] Visit {self.visits}: {here.name}");

        if self.visits >= self.max_visits {
            print(f"  [{self.label}] Max visits reached");
            disengage;
        }

        visit [-->];
    }
}

# ===== Part 8: Multiple Walkers on Same Graph =====
walker Counter {
    has total: int = 0;

    can start with `root entry {
        visit [-->];
    }

    can count_task with Task entry {
        self.total += 1;
        visit [-->];
    }
}

walker Analyzer {
    has high_priority: int = 0;
    has low_priority: int = 0;

    can start with `root entry {
        visit [-->];
    }

    can analyze with Task entry {
        if here.priority > 5 {
            self.high_priority += 1;
        } else {
            self.low_priority += 1;
        }
        visit [-->];
    }
}

# ===== Part 9: Walker Spawn Syntax Variations =====
walker SyntaxDemo {
    can start with `root entry {
        print("SyntaxDemo: Demonstrating all spawn syntaxes");
    }
}

# ===== Execution and Tests =====
with entry {
    print("=== 1. Building Test Graph ===");
    # Build tree structure:
    #     root
    #      |
    #    Task1 (p=10)
    #    /   \
    # Task2  Task3 (p=3)
    #  (p=5)   |
    #          Task4 (p=8)

    task1 = Task(name="Task1", priority=10);
    task2 = Task(name="Task2", priority=5);
    task3 = Task(name="Task3", priority=3);
    task4 = Task(name="Task4", priority=8);

    root ++> task1;
    task1 ++> task2;
    task1 ++> task3;
    task3 ++> task4;

    print("Graph: root -> Task1 -> Task2");
    print("                 \\-> Task3 -> Task4\n");

    print("=== 2. Basic spawn Keyword ===");
    # Most common form: node spawn Walker()
    root spawn BasicSpawn();

    print("\n=== 3. Depth-First Spatial Call (:>) ===");
    # Depth-first traversal: goes deep before wide
    # Expected: Task1, Task2, Task3, Task4
    root spawn :> DepthFirst;

    print("\n=== 4. Breadth-First Spatial Call (|>) ===");
    # Breadth-first traversal: goes level by level
    # Expected: Task1, Task2, Task3, Task4
    walker_bf = root spawn |> BreadthFirst;
    print(f"  Order visited: {walker_bf.order}");

    print("\n=== 5. Walker Return Values ===");
    # Walker instance is returned, can access state
    collector = root spawn DataCollector();
    print(f"  Collected: {collector.collected}");
    print(f"  Sum of priorities: {collector.sum}");

    print("\n=== 6. Walker Spawned from Nodes ===");
    # Walkers can spawn other walkers from any node
    root spawn NodeSpawner();

    print("\n=== 7. Walker Construction with Arguments ===");
    # Create walker with initial state
    w1 = root spawn ConstructedWalker(label="Alpha", max_visits=2);
    print(f"  Walker visited {w1.visits} tasks");

    print("\n=== 8. Multiple Walkers on Same Graph ===");
    # Different walkers can analyze same graph structure
    counter = root spawn Counter();
    analyzer = root spawn Analyzer();

    print(f"  Counter: {counter.total} tasks");
    print(f"  Analyzer: {analyzer.high_priority} high, {analyzer.low_priority} low priority");

    print("\n=== 9. Spawn Syntax Variations ===");
    # Variation 1: Basic spawn
    root spawn SyntaxDemo();

    # Variation 2: Depth-first spawn
    root spawn :> SyntaxDemo;

    # Variation 3: Breadth-first spawn
    root spawn |> SyntaxDemo;

    # Variation 4: Walker-first syntax (less common)
    SyntaxDemo() spawn root;

    # Variation 5: With constructed walker
    demo = SyntaxDemo();
    root spawn demo;

    print("\n=== 10. Traversal Strategy Comparison ===");
    # Build a deeper tree to see difference
    a = Task(name="A", priority=1);
    b = Task(name="B", priority=2);
    c = Task(name="C", priority=3);
    d = Task(name="D", priority=4);
    e = Task(name="E", priority=5);

    root ++> a;
    a ++> b;
    a ++> c;
    b ++> d;
    b ++> e;

    print("New graph: root -> A -> B -> D");
    print("                    \\   \\-> E");
    print("                     \\-> C\n");

    print("  Depth-first (:>):");
    df = root spawn :> SimpleWalker;
    print(f"    Visited: {df.visited_names}");

    print("\n  Breadth-first (|>):");
    bf = root spawn |> SimpleWalker;
    print(f"    Visited: {bf.visited_names}");

    print("\n✓ Object spatial calls demonstrated!");
}
from __future__ import annotations
from jaclang.runtimelib.builtin import *
from jaclang import JacMachineInterface as _jl

class Task(_jl.Node):
    name: str
    priority: int = 0

class SimpleWalker(_jl.Walker):
    visited_names: list = _jl.field(factory=lambda: [])

    @_jl.entry
    def start(self, here: _jl.Root) -> None:
        print('SimpleWalker: Starting at root')
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

    @_jl.entry
    def process(self, here: Task) -> None:
        self.visited_names.append(here.name)
        print(f'  Processing: {here.name} (priority {here.priority})')
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

class BasicSpawn(_jl.Walker):

    @_jl.entry
    def start(self, here: _jl.Root) -> None:
        print('BasicSpawn: started at root')
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

    @_jl.entry
    def handle_task(self, here: Task) -> None:
        print(f'  BasicSpawn at: {here.name}')
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

class DepthFirst(_jl.Walker):
    depth: int = 0

    @_jl.entry
    def start(self, here: _jl.Root) -> None:
        print('DepthFirst: root')
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

    @_jl.entry
    def process(self, here: Task) -> None:
        self.depth += 1
        print(f'  Depth-first [{self.depth}]: {here.name}')
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

class BreadthFirst(_jl.Walker):
    order: list = _jl.field(factory=lambda: [])

    @_jl.entry
    def start(self, here: _jl.Root) -> None:
        print('BreadthFirst: root')
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

    @_jl.entry
    def process(self, here: Task) -> None:
        self.order.append(here.name)
        print(f'  Breadth-first: {here.name}')
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

class DataCollector(_jl.Walker):
    collected: list = _jl.field(factory=lambda: [])
    sum: int = 0

    @_jl.entry
    def start(self, here: _jl.Root) -> None:
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

    @_jl.entry
    def collect(self, here: Task) -> None:
        self.collected.append(here.name)
        self.sum += here.priority
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

class NodeSpawner(_jl.Walker):

    @_jl.entry
    def start(self, here: _jl.Root) -> None:
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

    @_jl.entry
    def process(self, here: Task) -> None:
        print(f'  At {here.name}')
        if here.name == 'Task2':
            print('    Spawning SubWalker from Task2')
            _jl.spawn(here, SubWalker())
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

class SubWalker(_jl.Walker):

    @_jl.entry
    def start(self, here: Task) -> None:
        print(f'    SubWalker started at: {here.name}')
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

    @_jl.entry
    def handle(self, here: Task) -> None:
        print(f'      SubWalker processing: {here.name}')
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

class ConstructedWalker(_jl.Walker):
    label: str
    max_visits: int = 5
    visits: int = 0

    @_jl.entry
    def start(self, here: _jl.Root) -> None:
        print(f"ConstructedWalker '{self.label}' starting")
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

    @_jl.entry
    def process(self, here: Task) -> None:
        self.visits += 1
        print(f'  [{self.label}] Visit {self.visits}: {here.name}')
        if self.visits >= self.max_visits:
            print(f'  [{self.label}] Max visits reached')
            _jl.disengage(self)
            return
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

class Counter(_jl.Walker):
    total: int = 0

    @_jl.entry
    def start(self, here: _jl.Root) -> None:
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

    @_jl.entry
    def count_task(self, here: Task) -> None:
        self.total += 1
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

class Analyzer(_jl.Walker):
    high_priority: int = 0
    low_priority: int = 0

    @_jl.entry
    def start(self, here: _jl.Root) -> None:
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

    @_jl.entry
    def analyze(self, here: Task) -> None:
        if here.priority > 5:
            self.high_priority += 1
        else:
            self.low_priority += 1
        _jl.visit(self, _jl.refs(_jl.Path(here)._out().visit()))

class SyntaxDemo(_jl.Walker):

    @_jl.entry
    def start(self, here: _jl.Root) -> None:
        print('SyntaxDemo: Demonstrating all spawn syntaxes')
print('=== 1. Building Test Graph ===')
task1 = Task(name='Task1', priority=10)
task2 = Task(name='Task2', priority=5)
task3 = Task(name='Task3', priority=3)
task4 = Task(name='Task4', priority=8)
_jl.connect(left=_jl.root(), right=task1)
_jl.connect(left=task1, right=task2)
_jl.connect(left=task1, right=task3)
_jl.connect(left=task3, right=task4)
print('Graph: root -> Task1 -> Task2')
print('                 \\-> Task3 -> Task4\n')
print('=== 2. Basic spawn Keyword ===')
_jl.spawn(_jl.root(), BasicSpawn())
print('\n=== 3. Depth-First Spatial Call (:>) ===')
_jl.spawn(_jl.root(), DepthFirst())
print('\n=== 4. Breadth-First Spatial Call (|>) ===')
walker_bf = _jl.spawn(_jl.root(), BreadthFirst())
print(f'  Order visited: {walker_bf.order}')
print('\n=== 5. Walker Return Values ===')
collector = _jl.spawn(_jl.root(), DataCollector())
print(f'  Collected: {collector.collected}')
print(f'  Sum of priorities: {collector.sum}')
print('\n=== 6. Walker Spawned from Nodes ===')
_jl.spawn(_jl.root(), NodeSpawner())
print('\n=== 7. Walker Construction with Arguments ===')
w1 = _jl.spawn(_jl.root(), ConstructedWalker(label='Alpha', max_visits=2))
print(f'  Walker visited {w1.visits} tasks')
print('\n=== 8. Multiple Walkers on Same Graph ===')
counter = _jl.spawn(_jl.root(), Counter())
analyzer = _jl.spawn(_jl.root(), Analyzer())
print(f'  Counter: {counter.total} tasks')
print(f'  Analyzer: {analyzer.high_priority} high, {analyzer.low_priority} low priority')
print('\n=== 9. Spawn Syntax Variations ===')
_jl.spawn(_jl.root(), SyntaxDemo())
_jl.spawn(_jl.root(), SyntaxDemo())
_jl.spawn(_jl.root(), SyntaxDemo())
_jl.spawn(SyntaxDemo(), _jl.root())
demo = SyntaxDemo()
_jl.spawn(_jl.root(), demo)
print('\n=== 10. Traversal Strategy Comparison ===')
a = Task(name='A', priority=1)
b = Task(name='B', priority=2)
c = Task(name='C', priority=3)
d = Task(name='D', priority=4)
e = Task(name='E', priority=5)
_jl.connect(left=_jl.root(), right=a)
_jl.connect(left=a, right=b)
_jl.connect(left=a, right=c)
_jl.connect(left=b, right=d)
_jl.connect(left=b, right=e)
print('New graph: root -> A -> B -> D')
print('                    \\   \\-> E')
print('                     \\-> C\n')
print('  Depth-first (:>):')
df = _jl.spawn(_jl.root(), SimpleWalker())
print(f'    Visited: {df.visited_names}')
print('\n  Breadth-first (|>):')
bf = _jl.spawn(_jl.root(), SimpleWalker())
print(f'    Visited: {bf.visited_names}')
print('\n✓ Object spatial calls demonstrated!')
Jac Grammar Snippet
await_expr: KW_AWAIT? pipe_call
pipe_call: (PIPE_FWD | A_PIPE_FWD | KW_SPAWN)? atomic_chain

Description

Object spatial calls are the mechanisms for spawning and executing walkers on graph nodes. The spawn operator creates walker instances and launches them at specified nodes, enabling the "computation flows to data" paradigm.

What is Walker Spawning?

Spawning a walker means creating a walker instance and starting its execution at a specific node in the graph. Think of it like deploying an agent that will traverse your data structure, performing operations at each location it visits.

Basic spawn Syntax

The most common form is shown on line 204: root spawn BasicSpawn().

This breaks down as: - root - the starting node (where the walker begins) - spawn - the operator that launches the walker - BasicSpawn() - creates a new walker instance

When this executes: 1. A new BasicSpawn walker is created 2. The walker starts at the root node 3. The walker's matching entry ability executes (lines 26-28) 4. The walker processes its visit queue until empty

Understanding the Visit Queue

Walkers maintain a FIFO (first-in-first-out) queue of nodes to visit. This is fundamental to understanding how walkers traverse graphs:

graph TD
    A[Walker spawns at root] --> B[Root entry ability executes]
    B --> C[visit adds nodes to queue]
    C --> D[Process first node in queue]
    D --> E[Node ability executes]
    E --> F{More in queue?}
    F -->|Yes| D
    F -->|No| G[Walker completes]

The visit queue always processes nodes in FIFO order - first queued, first visited. Lines 17-21 show this pattern: visit [-->] adds all outgoing nodes to the queue, and they're processed in the order they were added.

How visit Statements Control Traversal

Lines 14, 20, 28, 33 all use visit [-->] to queue nodes. The visit statement determines which nodes get added to the queue:

Visit Expression What Gets Queued Example Line
visit [-->]; All outgoing nodes 14, 20, 28
visit [->:Type:->]; Outgoing nodes via Type edges -
visit [<--]; All incoming nodes -
visit node_list; Specific nodes -

Once nodes are in the queue, they're always processed FIFO regardless of how they were added.

Walker Return Values

Lines 219-221 demonstrate accessing walker state after execution. The spawn operator returns the walker instance, allowing you to: - Access accumulated data (lines 71-72: collected and sum attributes) - Check walker state after traversal completes - Use walkers as data collectors or analyzers

Lines 70-83 define DataCollector, which accumulates data during traversal (lines 79-80). After spawning (line 219), the walker's attributes remain accessible.

Spawn Syntax Variations

Lines 240-255 demonstrate all valid spawn syntaxes:

Syntax Description Example Line
node spawn Walker() Most common form 242
node spawn :> Walker With :> operator, walker type 245
node spawn |> Walker With |> operator, walker type 248
Walker() spawn node Walker-first syntax 251
Pre-constructed Create walker, then spawn 254-255

Important: The :> and |> operators take the walker TYPE, not a constructed instance. Use root spawn :> Walker, NOT root spawn :> Walker().

Walker Spawned from Nodes

Lines 85-112 show walkers spawning other walkers. Line 96 demonstrates: here spawn SubWalker().

The execution flow: 1. NodeSpawner visits Task2 (line 92) 2. Line 96: A new SubWalker spawns at the current node (here) 3. The SubWalker starts its own traversal from Task2 4. Both walkers can run independently

graph LR
    R[Root] --> T1[Task1]
    T1 --> T2[Task2]
    T1 --> T3[Task3]
    T3 --> T4[Task4]

    style T2 fill:#c62828,stroke:#fff,color:#fff

    Note[SubWalker spawns here at Task2]

This enables hierarchical processing where parent walkers spawn child walkers at interesting nodes.

Walker Construction with Arguments

Lines 114-136 show parameterized walkers. Line 229 demonstrates: root spawn ConstructedWalker(label="Alpha", max_visits=2).

The walker is initialized with: - label="Alpha" sets the walker's label (line 116) - max_visits=2 limits how many nodes it visits (line 117)

Lines 125-135 show the walker using these parameters to control its behavior, stopping after reaching max_visits via disengage (line 131).

Multiple Walkers on Same Graph

Lines 232-238 demonstrate running different walkers on the same graph structure. Each walker traverses independently: - Counter (lines 139-150) counts total tasks - Analyzer (lines 152-168) categorizes by priority

This demonstrates separation of data and computation - the graph structure is static, but different walkers extract different information.

Building Graph Structures for Traversal

Lines 179-200 build a test graph structure:

graph TD
    Root --> T1[Task1 p=10]
    T1 --> T2[Task2 p=5]
    T1 --> T3[Task3 p=3]
    T3 --> T4[Task4 p=8]

Lines 194-197 create the edges using the ++> operator: - Line 194: root ++> task1 connects root to Task1 - Line 195: task1 ++> task2 connects Task1 to Task2 - Lines 196-197: Create the remaining connections

This structure is then traversed by various walkers throughout the example.

Comparing Traversal Approaches

Lines 257-281 demonstrate how different visit patterns affect traversal order. The key insight: traversal order is controlled by HOW you structure your visit statements, not by the spawn operator used.

Both depth-first and breadth-first styles process the queue FIFO, but they add nodes to the queue differently: - Depth-first effect: Add child nodes to front of queue - Breadth-first effect: Add child nodes to end of queue (default)

Spawn Points and Starting Context

Walkers can spawn from different locations:

Spawn Point Usage Example Line
root spawn W() Start at root node 204, 214, 219
here spawn W() Start at current node 96
node_var spawn W() Start at specific node -

The spawn point becomes the walker's initial here reference, and the matching entry ability executes for that node type.

The Walker Execution Model

When a walker spawns, this sequence occurs:

  1. Initialization: Walker instance created (if using Walker() syntax)
  2. Entry ability: Matching entry ability for spawn node type executes
  3. Visit queue: Empty FIFO queue created
  4. Queue population: visit statements add nodes to queue
  5. FIFO processing: Walker processes queued nodes in order
  6. Node abilities: For each visited node:
  7. Walker's matching entry abilities execute
  8. Node's abilities (if any) execute
  9. More nodes may be queued via visit statements
  10. Completion: When queue empties, exit abilities execute
  11. Return: Walker instance returned to spawn caller

Practical Patterns

Pattern 1: Data Collection (lines 70-83, 219-221)

Pattern 2: Graph Search (stopping early with disengage)

Pattern 3: Multi-Walker Analysis (lines 232-238)

Pattern 4: Hierarchical Processing (lines 85-112)

Common Mistakes to Avoid

  1. Wrong operator syntax:
  2. ✗ Wrong: root spawn :> Walker()
  3. ✓ Right: root spawn :> Walker

  4. Assuming operators control traversal:

  5. ✗ Wrong assumption: :> does depth-first automatically
  6. ✓ Reality: Queue is FIFO; control traversal with visit logic

  7. Forgetting visit statements:

  8. Without visit [-->], walker only executes on spawn node
  9. No visits = no traversal

  10. Not capturing return value:

  11. root spawn Collector(); loses walker state
  12. result = root spawn Collector(); can access collected data

The Computation-to-Data Paradigm

Object spatial calls invert traditional programming:

Traditional approach (data comes to computation):

OSP approach (computation goes to data):

This inversion enables: - Locality: Computation executes where data resides - Persistence: Graph structure persists via root connectivity - Distribution: Walkers can traverse distributed graphs - Decoupling: Walker logic independent of graph structure - Reusability: Same walker works on different graph topologies

Object spatial calls are the fundamental execution primitive of Jac's Object-Spatial Programming model, enabling mobile computation that flows through persistent graph structures.