Implementations#
Code Example
Runnable Example in Jac and JacLib
"""Implementations: Forward declarations and impl blocks for deferred definitions."""
# Forward declarations
def compute(x: int, y: int) -> int;
obj Vehicle;
enum Priority;
obj Calculator {
has total: float = 0.0;
def add(value: float);
def subtract(value: float);
def multiply(value: float);
def get_result -> float;
}
# Implement function
impl compute(x: int, y: int) -> int {
return x + y;
}
# Implement object
impl Vehicle {
has name: str = "Car";
has speed: int = 0;
def accelerate { self.speed += 10; }
}
# Implement enum
impl Priority {
LOW = 1,
MEDIUM = 2,
HIGH = 3
}
# Implement object methods
impl Calculator.add(value: float) {
self.total += value;
}
impl Calculator.subtract(value: float) {
self.total -= value;
}
impl Calculator.multiply(value: float) {
self.total *= value;
}
impl Calculator.get_result -> float {
return self.total;
}
with entry {
# Test implementations
result = compute(5, 3);
v = Vehicle();
v.accelerate();
p = Priority.HIGH;
calc = Calculator();
calc.add(10.5);
calc.multiply(2.0);
calc.subtract(5.0);
print(result, v.name, v.speed, p.value);
print("Calculator result:", calc.get_result());
}
Jac Grammar Snippet
Description
Implementation Blocks in Jac
Implementation blocks (impl
) provide bodies for forward-declared elements, separating interface declarations from their implementations. This pattern enables modular code organization and resolves circular dependencies.
Forward Declarations
Lines 4-6 show forward declarations using semicolons to declare signatures without bodies. Line 4: Function signature without implementation Line 5: Object declaration without members Line 6: Enum declaration without values
Forward declarations establish names and interfaces early, allowing references before full implementation.
Function Implementation
Lines 9-11 implement the forward-declared function. The impl
keyword introduces the implementation block. The signature must match the forward declaration exactly.
Object Implementation
Lines 14-18 implement the forward-declared object. The implementation adds the object's structure: member variables and methods.
Enum Implementation
Lines 21-25 implement the forward-declared enum. Enum implementations provide member names and their associated values.
Using Implemented Elements
Lines 27-35 demonstrate using the implemented elements. All elements work normally after implementation, as if they were defined in a single step.
Forward Declaration and Implementation Pattern
flowchart TD
Start([Code Organization]) --> Forward[Forward Declarations<br/>Signatures Only]
Forward --> Refs[Can Reference<br/>in Other Code]
Refs --> Impl[Implementation Blocks<br/>impl keyword]
Impl --> Full[Fully Defined<br/>Elements]
Full --> Use[Use in Code]
Use Cases
Use Case | Benefit | Example |
---|---|---|
Circular dependencies | Break dependency cycles | Two objects referencing each other |
Interface/Implementation separation | Clear API boundaries | Public signatures, private implementations |
Large codebases | Organize related code | Headers and implementations in different sections |
Code generation | Stable interfaces | Generated signatures, manual implementations |
Declaration vs Implementation Comparison
Aspect | Forward Declaration | Implementation Block |
---|---|---|
Keyword | def , obj , enum |
impl |
Ends with | Semicolon (; ) |
Block ({ } ) |
Contains | Signature only | Full definition |
Purpose | Establish interface | Provide functionality |
Location | Typically at top | Later in file or separate file |
Implementation Flow Example
flowchart LR
FD1[def compute;] --> Ref[Code can reference compute]
FD2[obj Vehicle;] --> Ref
FD3[enum Priority;] --> Ref
Ref --> I1["impl compute with body"]
Ref --> I2["impl Vehicle with members"]
Ref --> I3["impl Priority with values"]
I1 --> Ready[All Elements Ready]
I2 --> Ready
I3 --> Ready
Separating Implementations Across Files
One of the most powerful uses of implementation blocks is organizing code across multiple files. This pattern keeps interface declarations in one file while placing implementations in separate files, similar to header/source file separation in C/C++.
Running the program:
The Jac compiler automatically links declarations with their implementations across files. This pattern provides several benefits:
- Clear API boundaries: The main file shows the public interface without implementation details
- Easier maintenance: Related implementations can be grouped together
- Better collaboration: Different developers can work on interfaces and implementations separately
- Reduced merge conflicts: Interface changes and implementation changes are isolated
Common Patterns
Separating interface from implementation:
Resolving circular dependencies:
Organizing complex types:
Key Points
- Forward declarations establish names without full definitions
- Implementation blocks provide the actual functionality
- Signatures must match exactly between declaration and implementation
- Useful for circular dependencies and code organization
- Similar to header/source separation in C/C++
- Elements can be referenced after forward declaration, before implementation
- All types (functions, objects, enums) support this pattern