Skip to content

Chapter 10: Walkers and Abilities#

Walkers are the heart of Object-Spatial Programming - they are mobile computational entities that traverse your graph and execute code at each location. Combined with abilities, they enable reactive, event-driven programming where computation happens exactly where and when it's needed.

Mobile Computation Philosophy

Unlike traditional functions that operate on data passed to them, walkers travel to where data lives, making computation truly spatial and enabling powerful distributed processing patterns.

Walker Creation#

What are Walkers?

Walkers are special objects that can move through your graph, carrying state and executing abilities when they encounter different types of nodes and edges.

Basic Walker Declaration#

Simple Walker

# Simple walker for visiting nodes
walker MessageDelivery {
    has message: str;
    has delivery_count: int = 0;
    has visited_locations: list[str] = [];

    # Regular methods work like normal
    def get_status() -> str {
        return f"Delivered {self.delivery_count} messages to {len(self.visited_locations)} locations";
    }
}

with entry {
    # Create walker instance (but don't activate it yet)
    messenger = MessageDelivery(message="Hello from the principal!");

    # Check initial state
    print(f"Initial status: {messenger.get_status()}");
}
class MessageDelivery:
    def __init__(self, message: str):
        self.message = message
        self.delivery_count = 0
        self.visited_locations = []

    def get_status(self) -> str:
        return f"Delivered {self.delivery_count} messages to {len(self.visited_locations)} locations"

class Student:
    def __init__(self, name: str):
        self.name = name
        self.messages = []

class Teacher:
    def __init__(self, name: str, subject: str):
        self.name = name
        self.subject = subject

if __name__ == "__main__":
    # Create messenger instance
    messenger = MessageDelivery(message="Hello from the principal!")

    # Check initial state
    print(f"Initial status: {messenger.get_status()}")

Ability Definitions and Triggers#

Event-Driven Execution

Abilities are methods that execute automatically when certain events occur during graph traversal. They create reactive, context-aware behavior.

Entry and Exit Abilities#

Basic Abilities

# Basic classroom nodes
node Student {
    has name: str;
    has messages: list[str] = [];
}

node Teacher {
    has name: str;
    has subject: str;
}

walker MessageDelivery {
    has message: str;
    has delivery_count: int = 0;
    has visited_locations: list[str] = [];

    # Entry ability - triggered when entering any Student
    can deliver_to_student with Student entry {
        print(f"Delivering message to student {here.name}");
        here.messages.append(self.message);
        self.delivery_count += 1;
        self.visited_locations.append(here.name);
    }

    # Entry ability - triggered when entering any Teacher
    can deliver_to_teacher with Teacher entry {
        print(f"Delivering message to teacher {here.name} ({here.subject})");
        # Teachers just acknowledge the message
        print(f"  {here.name} says: 'Message received!'");
        self.delivery_count += 1;
        self.visited_locations.append(here.name);
    }

    # Exit ability - triggered when leaving any node
    can log_visit with entry {
        node_type = type(here).__name__;
        print(f"  Visited {node_type}");
    }
}

with entry {
    # Create simple classroom
    alice = root ++> Student(name="Alice");
    bob = root ++> Student(name="Bob");
    ms_smith = root ++> Teacher(name="Ms. Smith", subject="Math");

    # Create and activate messenger
    messenger = MessageDelivery(message="School assembly at 2 PM");

    # Spawn walker on Alice - this activates it
    alice[0] spawn messenger;

    # Check Alice's messages
    print(f"Alice's messages: {alice[0].messages}");
}
class MessageDelivery:
    def __init__(self, message: str):
        self.message = message
        self.delivery_count = 0
        self.visited_locations = []

    def deliver_to_student(self, student):
        print(f"Delivering message to student {student.name}")
        student.messages.append(self.message)
        self.delivery_count += 1
        self.visited_locations.append(student.name)

    def deliver_to_teacher(self, teacher):
        print(f"Delivering message to teacher {teacher.name} ({teacher.subject})")
        print(f"  {teacher.name} says: 'Message received!'")
        self.delivery_count += 1
        self.visited_locations.append(teacher.name)

    def visit_node(self, node):
        node_type = type(node).__name__
        print(f"  Visited {node_type}")

        # Manually check type and call appropriate method
        if isinstance(node, Student):
            self.deliver_to_student(node)
        elif isinstance(node, Teacher):
            self.deliver_to_teacher(node)

if __name__ == "__main__":
    # Create simple classroom
    alice = Student("Alice")
    bob = Student("Bob")
    ms_smith = Teacher("Ms. Smith", "Math")

    # Create and use messenger manually
    messenger = MessageDelivery("School assembly at 2 PM")

    # Manually visit nodes (simulating walker spawn)
    messenger.visit_node(alice)

    # Check Alice's messages
    print(f"Alice's messages: {alice.messages}")

Walker Spawn and Visit#

Graph Traversal

Walkers move through graphs using spawn (to start) and visit (to continue to connected nodes). This enables complex traversal patterns with simple syntax.

Basic Traversal Patterns#

Classroom Message Delivery

# Basic classroom nodes
node Student {
    has name: str;
    has messages: list[str] = [];
}

node Teacher {
    has name: str;
    has subject: str;
}

walker MessageDelivery {
    has message: str;
    has delivery_count: int = 0;
    has visited_locations: list[str] = [];

    # Entry ability - triggered when entering any Student
    can deliver_to_student with Student entry {
        print(f"Delivering message to student {here.name}");
        here.messages.append(self.message);
        self.delivery_count += 1;
        self.visited_locations.append(here.name);
    }

    # Entry ability - triggered when entering any Teacher
    can deliver_to_teacher with Teacher entry {
        print(f"Delivering message to teacher {here.name} ({here.subject})");
        # Teachers just acknowledge the message
        print(f"  {here.name} says: 'Message received!'");
        self.delivery_count += 1;
        self.visited_locations.append(here.name);
    }

    # Exit ability - triggered when leaving any node
    can log_visit with entry {
        node_type = type(here).__name__;
        print(f"  Visited {node_type}");
    }
}

edge InClass {
    has room: str;
}

walker ClassroomMessenger {
    has announcement: str;
    has rooms_visited: set[str] = {};
    has people_reached: int = 0;

    can deliver_student with Student entry {
        print(f" Student {here.name}: {self.announcement}");
        self.people_reached += 1;

        # Continue to connected nodes
        visit [-->];
    }

    can deliver_teacher with Teacher entry {
        print(f" Teacher {here.name}: {self.announcement}");
        self.people_reached += 1;

        # Continue to connected nodes
        visit [-->];
    }

    can track_room with InClass entry {
        room = here.room;
        if room not in self.rooms_visited {
            self.rooms_visited.add(room);
            print(f" Now in {room}");
        }
    }

    can summarize with Student exit {
        # Only report once at the end
        if len([-->]) == 0 {  # At a node with no outgoing connections
            print(f" Delivery complete!");
            print(f"   People reached: {self.people_reached}");
            print(f"   Rooms visited: {list(self.rooms_visited)}");
        }
    }
}

with entry {
    # Create classroom structure
    alice = root ++> Student(name="Alice");
    bob = root ++> Student(name="Bob");
    charlie = root ++> Student(name="Charlie");
    ms_jones = root ++> Teacher(name="Ms. Jones", subject="Science");

    # Connect them in the same classroom
    alice +>:InClass(room="Room 101"):+> bob;
    bob +>:InClass(room="Room 101"):+> charlie;
    charlie +>:InClass(room="Room 101"):+> ms_jones;

    # Send a message through the classroom
    messenger = ClassroomMessenger(announcement="Fire drill in 5 minutes");
    alice[0] spawn messenger;
}
class InClass:
    def __init__(self, from_node, to_node, room: str):
        self.from_node = from_node
        self.to_node = to_node
        self.room = room

class ClassroomMessenger:
    def __init__(self, announcement: str):
        self.announcement = announcement
        self.rooms_visited = set()
        self.people_reached = 0
        self.visited_nodes = set()

    def deliver_to_student(self, student):
        print(f" Student {student.name}: {self.announcement}")
        self.people_reached += 1

    def deliver_to_teacher(self, teacher):
        print(f" Teacher {teacher.name}: {self.announcement}")
        self.people_reached += 1

    def track_room(self, edge):
        if edge.room not in self.rooms_visited:
            self.rooms_visited.add(edge.room)
            print(f"   Now in {edge.room}")

    def visit_network(self, node, connections):
        # Avoid infinite loops
        if node in self.visited_nodes:
            return

        self.visited_nodes.add(node)

        # Process current node
        if isinstance(node, Student):
            self.deliver_to_student(node)
        elif isinstance(node, Teacher):
            self.deliver_to_teacher(node)

        # Visit connected nodes
        for edge in connections.get(node, []):
            self.track_room(edge)
            self.visit_network(edge.to_node, connections)

        # Check if we're at the end
        if not connections.get(node, []):
            print(f" Delivery complete!")
            print(f"   People reached: {self.people_reached}")
            print(f"   Rooms visited: {list(self.rooms_visited)}")

if __name__ == "__main__":
    # Create classroom structure
    alice = Student("Alice")
    bob = Student("Bob")
    charlie = Student("Charlie")
    ms_jones = Teacher("Ms. Jones", "Science")

    # Create connections manually
    connections = {
        alice: [InClass(alice, bob, "Room 101")],
        bob: [InClass(bob, charlie, "Room 101")],
        charlie: [InClass(charlie, ms_jones, "Room 101")],
        ms_jones: []
    }

    # Send message through classroom
    messenger = ClassroomMessenger("Fire drill in 5 minutes")
    messenger.visit_network(alice, connections)

Advanced Traversal Control#

Selective Message Delivery

import random;

node Student {
    has name: str;
    has grade_level: int;
    has messages: list[str] = [];
}

edge StudyGroup {
    has subject: str;
}

walker AttendanceChecker {
    has present_students: list[str] = [];
    has absent_students: list[str] = [];
    has max_checks: int = 5;
    has checks_done: int = 0;

    can check_attendance with Student entry {
        self.checks_done += 1;

        # Simulate checking if student is present (random for demo)
        is_present = random.choice([True, False]);

        if is_present {
            print(f"{here.name} is present");
            self.present_students.append(here.name);
        } else {
            print(f"{here.name} is absent");
            self.absent_students.append(here.name);
        }

        # Control flow based on conditions
        if self.checks_done >= self.max_checks {
            print(f"Reached maximum checks ({self.max_checks})");
            self.report_final();
            disengage;  # Stop the walker
        }

        # Skip if no more connections
        connections = [-->];
        if not connections {
            print("No more students to check");
            self.report_final();
            disengage;
        }

        # Continue to next student
        visit [-->];
    }

    def report_final() -> None {
        print(f" Attendance Report:");
        print(f"   Present: {self.present_students}");
        print(f"   Absent: {self.absent_students}");
        print(f"   Total checked: {self.checks_done}");
    }
}

with entry {
    # Create a chain of students
    alice = root ++> Student(name="Alice", grade_level=9);
    bob = alice ++> Student(name="Bob", grade_level=9);
    charlie = bob ++> Student(name="Charlie", grade_level=9);
    diana = charlie ++> Student(name="Diana", grade_level=9);
    eve = diana ++> Student(name="Eve", grade_level=9);

    # Start attendance check
    checker = AttendanceChecker(max_checks=3);
    alice[0] spawn checker;
}
class StudyGroup:
    def __init__(self, from_node, to_node, subject: str):
        self.from_node = from_node
        self.to_node = to_node
        self.subject = subject

class Student:
    def __init__(self, name: str, grade_level: int):
        self.name = name
        self.grade_level = grade_level
        self.messages = []

class GradeSpecificMessenger:
    def __init__(self, message: str, target_grade: int):
        self.message = message
        self.target_grade = target_grade
        self.delivered_to = []
        self.visited = set()

    def visit_student(self, student, connections):
        if student in self.visited:
            return

        self.visited.add(student)

        if student.grade_level == self.target_grade:
            print(f"Delivering to {student.name} (Grade {student.grade_level}): {self.message}")
            student.messages.append(self.message)
            self.delivered_to.append(student.name)
        else:
            print(f"Skipping {student.name} (Grade {student.grade_level}) - not target grade")

        # Visit connected students
        for edge in connections.get(student, []):
            print(f"  Moving through {edge.subject} study group")
            self.visit_student(edge.to_node, connections)

        # Report if at end
        if not connections.get(student, []):
            print(f" Delivery Summary:")
            print(f"   Target: Grade {self.target_grade} students")
            print(f"   Message: '{self.message}'")
            print(f"   Delivered to: {self.delivered_to}")

if __name__ == "__main__":
    # Create multi-grade study network
    alice = Student("Alice", 9)
    bob = Student("Bob", 10)
    charlie = Student("Charlie", 9)
    diana = Student("Diana", 11)

    # Create connections
    connections = {
        alice: [StudyGroup(alice, bob, "Math")],
        bob: [StudyGroup(bob, charlie, "Science")],
        charlie: [StudyGroup(charlie, diana, "History")],
        diana: []
    }

    # Send grade-specific message
    messenger = GradeSpecificMessenger(
        "Grade 9 field trip permission slips due Friday!",
        9
    )

    messenger.visit_student(alice, connections)

    # Check who got the message
    print(f"Alice's messages: {alice.messages}")
    print(f"Bob's messages: {bob.messages}")
    print(f"Charlie's messages: {charlie.messages}")

Walker Control Flow#

Traversal Control

Walkers can control their movement through the graph using special statements like visit and disengage.

Controlling Walker Behavior#

Smart Walker Control

node Student {
    has name: str;
    has grade_level: int;
}

walker AttendanceChecker {
    has present_students: list[str] = [];
    has absent_students: list[str] = [];
    has max_checks: int = 5;
    has checks_done: int = 0;

    can check_attendance with Student entry {
        self.checks_done += 1;

        # Simulate checking if student is present (random for demo)
        import random;
        is_present = random.choice([True, False]);

        if is_present {
            print(f"{here.name} is present");
            self.present_students.append(here.name);
        } else {
            print(f"{here.name} is absent");
            self.absent_students.append(here.name);
        }

        # Control flow based on conditions
        if self.checks_done >= self.max_checks {
            print(f"Reached maximum checks ({self.max_checks})");
            self.report_final();
            disengage;  # Stop the walker
        }

        # Skip if no more connections
        connections = [-->];
        if not connections {
            print("No more students to check");
            self.report_final();
            disengage;
        }

        # Continue to next student
        visit [-->];
    }

    def report_final() -> None {
        print(f" Attendance Report:");
        print(f"   Present: {self.present_students}");
        print(f"   Absent: {self.absent_students}");
        print(f"   Total checked: {self.checks_done}");
    }
}

with entry {
    # Create a chain of students
    alice = root ++> Student(name="Alice", grade_level=9);
    bob = alice ++> Student(name="Bob", grade_level=9);
    charlie = bob ++> Student(name="Charlie", grade_level=9);
    diana = charlie ++> Student(name="Diana", grade_level=9);
    eve = diana ++> Student(name="Eve", grade_level=9);

    # Start attendance check
    checker = AttendanceChecker(max_checks=3);
    alice[0] spawn checker;
}
import random

class AttendanceChecker:
    def __init__(self, max_checks: int = 5):
        self.present_students = []
        self.absent_students = []
        self.max_checks = max_checks
        self.checks_done = 0
        self.should_stop = False

    def check_student(self, student, connections):
        if self.should_stop:
            return

        self.checks_done += 1

        # Simulate checking if student is present
        is_present = random.choice([True, False])

        if is_present:
            print(f" {student.name} is present")
            self.present_students.append(student.name)
        else:
            print(f" {student.name} is absent")
            self.absent_students.append(student.name)

        # Control flow based on conditions
        if self.checks_done >= self.max_checks:
            print(f" Reached maximum checks ({self.max_checks})")
            self.report_final()
            return  # Stop checking

        # Continue to next student if available
        next_students = connections.get(student, [])
        if not next_students:
            print(" No more students to check")
            self.report_final()
            return

        # Visit next student
        for next_student in next_students:
            self.check_student(next_student, connections)

    def report_final(self):
        print(f" Attendance Report:")
        print(f"   Present: {self.present_students}")
        print(f"   Absent: {self.absent_students}")
        print(f"   Total checked: {self.checks_done}")

if __name__ == "__main__":
    # Create a chain of students
    alice = Student("Alice", 9)
    bob = Student("Bob", 9)
    charlie = Student("Charlie", 9)
    diana = Student("Diana", 9)
    eve = Student("Eve", 9)

    # Create connections (linear chain)
    connections = {
        alice: [bob],
        bob: [charlie],
        charlie: [diana],
        diana: [eve],
        eve: []
    }

    # Start attendance check
    checker = AttendanceChecker(max_checks=3)
    checker.check_student(alice, connections)

Key Concepts Summary#

Walker and Ability Fundamentals

  • Walkers are mobile computational entities that traverse graphs
  • Abilities are event-driven methods that execute automatically during traversal
  • Entry abilities trigger when a walker arrives at a node
  • Exit abilities trigger when a walker leaves a node
  • Spawn activates a walker at a specific starting location
  • Visit moves a walker to connected nodes
  • Disengage stops a walker's execution

Best Practices#

Walker Design Guidelines

  • Keep abilities focused: Each ability should have a single, clear purpose
  • Use descriptive names: Make it clear what each walker and ability does
  • Handle edge cases: Check for empty connections before visiting
  • Control traversal flow: Use conditions to avoid infinite loops
  • Report results: Use exit abilities to summarize walker activities
  • Manage state: Use walker properties to track progress and results

Key Takeaways#

What We've Learned

Walker System:

  • Mobile computation: Walkers bring processing directly to data locations
  • State management: Walkers carry their own state as they traverse
  • Traversal control: Fine-grained control over movement patterns
  • Spawning mechanism: Activate walkers at specific graph locations

Ability System:

  • Event-driven execution: Abilities trigger automatically based on walker location
  • Entry/exit patterns: React to walker arrival and departure events
  • Context awareness: Abilities have access to current node (here) and walker state
  • Conditional execution: Abilities can include logic to control when they execute

Traversal Patterns:

  • Visit statements: Direct walker movement to connected nodes
  • Filtering support: Visit only nodes that match specific criteria
  • Flow control: Use disengage to stop walker execution
  • Recursive traversal: Walkers can spawn other walkers for complex patterns

Practical Applications:

  • Data processing: Process distributed data where it lives
  • Graph analysis: Analyze relationships and connections
  • Message delivery: Distribute information through networks
  • State propagation: Update related nodes based on changes

Try It Yourself

Master walkers and abilities by building: - A message delivery system that traverses social networks - An attendance checker that visits classrooms - A family tree explorer that analyzes relationships - A network analyzer that processes organizational structures

Remember: Walkers bring computation to data - think about how your processing can move through your graph!


Walkers and abilities are now part of your toolkit. Let's master advanced graph operations and sophisticated traversal patterns!