Class Archetype bodies#
Code Example
Runnable Example in Jac and JacLib
"""Archetype bodies: Member statements (has, static, methods, nested types)."""
"""This is a module-level docstring"""
# Note: This example uses 'obj' archetype where all 'has' fields are instance variables.
# In 'class' archetypes, 'has' fields with defaults become class variables (shared).
# See class_archetypes.jac for class vs obj comparison.
obj Vehicle {
"""Member docstring"""
# Has statements with type annotations (instance variables in obj)
has name: str,
year: int;
# Static members
static has count: int = 0;
# Access modifiers
has :pub public_id: str = "V123";
has :priv private_data: int = 0;
# Has with postinit initialization
has config: dict by postinit;
def postinit {
self.config = {"active": True};
Vehicle.count += 1;
}
# Instance methods
def display -> str {
return f"{self.year} {self.name}";
}
# Static methods
static def get_count -> int {
return Vehicle.count;
}
# Nested class
class Part {
has part_name: str;
}
# Inline Python
::py::
def py_method(self):
return "Python code"
::py::
}
with entry {
v1 = Vehicle(name="Car", year=2020);
v2 = Vehicle(name="Truck", year=2021);
print(v1.display(), v2.display(), Vehicle.get_count());
}
Jac Grammar Snippet
Description
Archetype bodies define the internal structure and behavior of objects, classes, nodes, edges, and walkers. The body enclosed in braces (lines 5-47) contains member statements that specify data fields, methods, nested types, and initialization logic.
Documentation Strings:
Lines 1 and 3 show module-level docstrings using triple quotes. Line 6 demonstrates a member docstring - string literals appearing before member statements provide inline documentation for the archetype and its members.
Has Statements - Declaring Fields:
Lines 9-10 show the has
keyword declaring data fields. Each field requires a type annotation using colon syntax (: type
). Multiple fields can be declared in a single statement separated by commas, terminated by a semicolon.
Important: class
vs obj
Behavior:
The example uses obj Vehicle
(line 5), where all has
fields automatically become instance variables. In contrast:
- In obj
archetypes: has
fields are instance variables by default (each instance gets its own copy)
- In class
archetypes: has
fields with defaults become class variables initially (shared across instances), but can be shadowed when assigned
Method Self Parameter:
- In obj
archetypes: Methods have implicit self
- it doesn't appear in the parameter list (e.g., def display() -> str { return self.year; }
)
- In class
archetypes: Methods require explicit self
parameter with type annotation (e.g., def display(self: MyClass) -> str { return self.year; }
)
See class_archetypes.md for detailed explanation
Declaration Pattern | Example | Meaning |
---|---|---|
Single field | has name: str; |
One field with type |
Multiple fields | has name: str, year: int; |
Multiple fields, one statement |
With default | has count: int = 0; |
Field with initial value |
With access | has :pub id: str = "V123"; |
Field with visibility modifier |
Static vs Instance Members:
Line 13 shows static has
creating a class-level attribute shared across all instances in both obj
and class
. Compare:
- Instance fields (lines 9-10 in
obj
): Each object gets its own copy, accessed viaself.name
- Static fields (line 13): One copy shared by all instances, accessed via
Vehicle.count
(line 24)
Line 33 demonstrates static def
for static methods - no self
parameter in both class
and obj
archetypes. For instance methods, obj
archetypes have implicit self
while class
archetypes require explicit self
with type annotation. Static methods are called on the archetype itself (line 53).
Access Modifiers:
Lines 16-17 control member visibility using colon-prefix syntax after has
:
:pub
- Public (accessible from anywhere):priv
- Private (restricted to this archetype):prot
- Protected (accessible to subclasses)
The modifier appears between has
and the field name.
Postinit Initialization:
Line 20 shows by postinit
- defers field initialization to the postinit
method. The special postinit
method (lines 22-25) executes after construction but before the object is returned. This enables:
- Computed field initialization (line 23)
- Cross-field validation
- Side effects like registration (line 24 increments static counter)
- Setup requiring fully initialized state
Methods:
graph TD
A[Method Types] --> B[Instance Methods]
A --> C[Static Methods]
B --> D["def name() { ... }"]
B --> E["Receives self"]
B --> F["Access instance data"]
C --> G["static def name() { ... }"]
C --> H["No self parameter"]
C --> I["Access static data"]
Line 28 shows an instance method with return type annotation (-> str
). Line 33 shows a static method returning class-level data.
Nested Archetypes:
Lines 38-40 demonstrate nesting - a class Part
defined inside Vehicle
. This creates logical grouping and namespace organization. Access nested types via parent name: Vehicle.Part()
.
Inline Python:
Lines 43-46 show embedding raw Python code using ::py::
delimiters. Code between markers executes as native Python, allowing:
- Python-specific methods
- Direct library usage
- Performance-critical sections
- Interop with Python codebases
Member Statement Categories:
Category | Keywords | Lines | Purpose |
---|---|---|---|
Data | has , static has |
9-10, 13, 16-17, 20 | State storage |
Behavior | def , static def |
22-25, 28-30, 33-35 | Operations |
Types | class , obj , etc. |
38-40 | Nested definitions |
Interop | ::py:: |
43-46 | Python integration |
Field Initialization Flow:
sequenceDiagram
participant C as Constructor
participant F as Fields
participant P as Postinit
participant R as Return
C->>F: Initialize fields with defaults
C->>F: Set constructor arguments
F->>P: Call postinit method
P->>F: Initialize postinit fields
P->>P: Execute setup logic
P->>R: Return fully initialized object
Line 50 calls the constructor with name
and year
. Fields get values, then postinit
runs (setting config
and incrementing count
), finally the object is returned.
Type Annotations:
All fields require type annotations (colon followed by type). Common types include:
- Primitives: str
, int
, float
, bool
- Collections: list
, dict
, set
, tuple
- Custom: Any archetype name
- Generic: list[str]
, dict[str, int]
Usage Patterns:
Line 50-51 show object creation - constructors accept named arguments matching field declarations. Line 53 demonstrates:
- Instance method calls: v1.display()
- Static method calls: Vehicle.get_count()
The output would be: 2020 Car 2021 Truck 2
(two displays and the count).