Connect expressions (OSP)#
Code Example
Runnable Example in Jac and JacLib
"""Connect expressions (OSP): Graph edge creation and connection operators."""
# ===== Node and Edge Definitions =====
node Person {
has name: str;
has age: int = 0;
}
node City {
has name: str;
}
edge LivesIn {
has years: int = 0;
}
edge Friend {
has since: int = 2020;
}
edge Colleague {
has department: str;
}
# ===== Test Walker =====
walker GraphBuilder {
can build with `root entry {
print("=== 1. Untyped Connect Operators ===");
# Forward (++>), Backward (<++), Bidirectional (<++>)
alice = Person(name="Alice", age=30);
bob = Person(name="Bob", age=25);
charlie = Person(name="Charlie", age=28);
alice ++> bob; # Forward: alice -> bob
print(f"Forward: {alice.name} ++> {bob.name}");
bob <++ charlie; # Backward: charlie -> bob
print(f"Backward: {bob.name} <++ {charlie.name} (edge from Charlie to Bob)");
alice <++> charlie; # Bidirectional: alice <-> charlie
print(f"Bidirectional: {alice.name} <++> {charlie.name} (both directions)");
print("\n=== 2. Typed Connect Operators ===");
# Forward (+>:Type:+>), Backward (<+:Type:<+), Bidirectional (<+:Type:+>)
diana = Person(name="Diana", age=32);
eve = Person(name="Eve", age=27);
frank = Person(name="Frank", age=29);
nyc = City(name="New York");
london = City(name="London");
diana +>:LivesIn:+> nyc; # Typed forward
print(f"Typed forward: {diana.name} +>:LivesIn:+> {nyc.name}");
eve <+:LivesIn:<+ london; # Typed backward: london -> eve
print(f"Typed backward: {eve.name} <+:LivesIn:<+ {london.name} (edge from London to Eve)");
diana <+:Friend:+> eve; # Typed bidirectional
print(f"Typed bidirectional: {diana.name} <+:Friend:+> {eve.name}");
print("\n=== 3. Edge Attribute Initialization ===");
# Initialize edge attributes during connect (all directions)
grace = Person(name="Grace", age=26);
henry = Person(name="Henry", age=31);
iris = Person(name="Iris", age=24);
grace +>: Friend(since=2015) :+> henry; # Forward with attributes
print(f"Forward with attrs: {grace.name} +>: Friend(since=2015) :+> {henry.name}");
henry <+: Friend(since=2018) :<+ iris; # Backward with attributes
print(f"Backward with attrs: {henry.name} <+: Friend(since=2018) :<+ {iris.name}");
grace <+: Colleague(department="Engineering") :+> iris; # Bidirectional with attributes
print(f"Bidirectional with attrs: {grace.name} <+: Colleague(department='Engineering') :+> {iris.name}");
print("\n=== 4. Chained Connections ===");
# Multiple connects in one expression
jack = Person(name="Jack", age=35);
kate = Person(name="Kate", age=29);
liam = Person(name="Liam", age=30);
mike = Person(name="Mike", age=33);
jack ++> kate ++> liam ++> mike; # Chain of connections
print(f"Chain: {jack.name} ++> {kate.name} ++> {liam.name} ++> {mike.name}");
print("\n=== 5. Inline Node Creation ===");
# Create nodes inline within connect expression
nina = Person(name="Nina", age=28);
nina ++> Person(name="InlineNode1", age=35); # Untyped with inline
nina +>:Friend:+> Person(name="InlineNode2", age=40); # Typed with inline
nina +>: Friend(since=2010) :+> Person(name="InlineNode3", age=45); # With attributes
print("Connected to 3 inline-created nodes (untyped, typed, with attrs)");
print("\n=== 6. Connect to Multiple Targets ===");
# One node connecting to multiple targets
oscar = Person(name="Oscar", age=27);
paula = Person(name="Paula", age=26);
quinn = Person(name="Quinn", age=24);
oscar ++> paula;
oscar ++> quinn;
oscar +>:Friend:+> Person(name="Rita", age=30);
print(f"Connected {oscar.name} to 3 different targets");
print("\n=== 7. Disconnect Operator ===");
# Grammar: disconnect_op: KW_DELETE edge_op_ref
# Syntax: node del [-->] target
print("Disconnect syntax: node del [-->] target (deletes edges from node to target)");
print("\n=== 8. Connect in Expressions ===");
# Connect expressions return values and can be used in larger expressions
steve = Person(name="Steve", age=45);
tina = Person(name="Tina", age=42);
steve ++> tina;
print(f"Connect used in expression: {steve.name} ++> {tina.name}");
print("\n✓ All connect expression features demonstrated!");
disengage;
}
}
# ===== Edge Traversal Demonstration =====
walker EdgeTraverser {
can traverse with `root entry {
print("\n=== Edge Traversal with Visit ===");
# Build a small graph demonstrating typed edges
a = Person(name="A", age=25);
b = Person(name="B", age=30);
c = Person(name="C", age=35);
root ++> a;
a +>: Friend(since=2010) :+> b;
a +>: Colleague(department="Sales") :+> c;
b +>: Friend(since=2015) :+> c;
print("Graph: root->A, A-Friend->B, A-Colleague->C, B-Friend->C");
print("Visiting all outgoing edges from root:");
visit [-->];
}
can traverse with Person entry {
print(f" Visited: {here.name}, age={here.age}");
}
}
# ===== Execution =====
with entry {
root spawn GraphBuilder();
root spawn EdgeTraverser();
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
|
Jac Grammar Snippet
Description
Connect Expressions (OSP)
Connect expressions are specialized operators for creating edges between nodes in graph structures. These operators are fundamental to Jac's Object-Spatial Programming (OSP), providing first-class syntax for spatial relationships.
Connect Operator Types
Direction | Untyped | Typed | With Attributes |
---|---|---|---|
Forward (A → B) | ++> |
+>:Type:+> |
+>: Type(...) :+> |
Backward (A ← B) | <++ |
<+:Type:<+ |
<+: Type(...) :<+ |
Bidirectional (A ↔ B) | <++> |
<+:Type:+> |
<+: Type(...) :+> |
Node and Edge Definitions (Lines 4-23)
The example defines nodes and edges for the graph:
Lines 4-7: Person
node with name
and age
attributes
Lines 9-11: City
node with name
attribute
Lines 13-15: LivesIn
edge with years
attribute
Lines 17-19: Friend
edge with since
attribute
Lines 21-23: Colleague
edge with department
attribute
Untyped Connect Operators (Lines 28-41)
These create generic edges without specifying an edge type:
Forward connection (Line 34): - Arrow points right: alice → bob - Line 35 prints confirmation
Backward connection (Line 37): - Arrow points left: charlie → bob (not bob → charlie!) - Line 38 notes the actual direction
Bidirectional connection (Line 40): - Creates alice → charlie AND charlie → alice - Line 41 confirms both directions created
Direction Flow Diagram
graph LR
A[alice] -->|++>| B[bob]
C[charlie] -->|<++| B
A <-->|<++>| C
style A fill:#1565c0,stroke:#fff,color:#fff
style B fill:#1565c0,stroke:#fff,color:#fff
style C fill:#1565c0,stroke:#fff,color:#fff
Typed Connect Operators (Lines 43-58)
These specify the edge archetype during connection:
Forward typed (Line 51):
- Creates LivesIn
edge from diana to nyc
- Edge type is explicitly LivesIn
- Line 52 shows the syntax
Backward typed (Line 54):
- Creates LivesIn
edge from london to eve (not eve to london!)
- Line 55 clarifies the actual direction
Bidirectional typed (Line 57):
- Creates Friend
edges in both directions
- Both diana → eve and eve → diana are Friend
type
Edge Attribute Initialization (Lines 60-73)
Initialize edge attributes during connection:
Forward with attributes (Line 66):
- Creates Friend
edge from grace to henry
- Sets since
attribute to 2015
- Line 67 shows the full syntax
Backward with attributes (Line 69):
- Creates Friend
edge from iris to henry
- Edge has since=2018
Bidirectional with attributes (Line 72):
- Creates Colleague
edges in both directions
- Both edges have department="Engineering"
Chained Connections (Lines 75-83)
Connect operators can be chained left-to-right:
Line 82: jack ++> kate ++> liam ++> mike;
- Creates path: jack → kate → liam → mike
- Evaluates left-to-right
- Line 83 shows the resulting chain
Chaining Visualization
graph LR
J[jack] --> K[kate]
K --> L[liam]
L --> M[mike]
Inline Node Creation (Lines 85-92)
Create nodes directly in connect expressions:
Line 89: nina ++> Person(name="InlineNode1", age=35);
- Creates a new Person
node inline
- Immediately connects nina to it
- No need for intermediate variable
Line 90: nina +>:Friend:+> Person(name="InlineNode2", age=40);
- Inline node creation with typed edge
Line 91: nina +>: Friend(since=2010) :+> Person(name="InlineNode3", age=45);
- Combines inline creation with edge attributes
Multiple Target Connections (Lines 94-103)
One node can connect to multiple targets:
Lines 100-102: - Oscar connects to three different nodes - Creates a hub/star pattern - Line 103 confirms all connections
Hub Pattern Diagram
graph TD
O[oscar] --> P[paula]
O --> Q[quinn]
O --> R[Rita]
Disconnect Operator (Lines 105-108)
Line 107: node del [-->] target
- Deletes edges from node to target
- Uses del
keyword with edge reference syntax
- Can specify edge type: node del [->:Type:->] target
Connect in Expressions (Lines 110-116)
Connect expressions return values and integrate with other expressions:
Lines 115-116: - Connect can be part of larger expressions - Returns a value that can be used in subsequent code
Edge Traversal Integration (Lines 123-146)
The example demonstrates how created edges are traversed:
Building the graph (Lines 133-136):
Traversing edges (Line 140): - Visits all outgoing edges from current node - Uses edge reference syntax (different from connect operators)
Traversal Flow
graph TD
Root --> A[Person A]
A -->|Friend since=2010| B[Person B]
A -->|Colleague Sales| C[Person C]
B -->|Friend since=2015| C
W[Walker] -.->|visit| A
W -.->|visit| B
W -.->|visit| C
Connect vs Edge References
Aspect | Connect Operators | Edge References |
---|---|---|
Purpose | CREATE edges | TRAVERSE edges |
Examples | ++> , +>:Type:+> |
[-->] , [->:Type:->] |
Usage | Graph construction | Visit statements, queries |
Action | Imperative (make connection) | Declarative (find connections) |
Context | Building graph structure | Navigating graph structure |
Common Graph Patterns
Linear chain (line 82):
Star/hub (lines 100-102):
Bidirectional network:
Heterogeneous typed graph (lines 134-136):
Operator Directionality Guide
Understanding arrow direction is critical:
Code | Actual Edge Direction | Memory Aid |
---|---|---|
a ++> b |
a → b | Arrow points to target |
a <++ b |
b → a | Arrow points away, so b → a |
a <++> b |
a ↔ b | Arrows both ways = both edges |
Best Practices
- Choose typed edges for clarity: Use
+>:Type:+>
when edge semantics matter - Initialize attributes during connect: Cleaner than setting attributes after
- Use backward operators intentionally:
<++
can improve code readability - Chain for linear structures: Paths and sequences benefit from chaining
- Inline creation for temporary nodes: Reduces variable clutter
- Document edge semantics: Comment complex graph structures
Integration with OSP
Connect expressions work seamlessly with walkers:
Building during traversal:
Conditional connections: