Skip to content

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());
}
"""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());
}
"""Implementations: Forward declarations and impl blocks for deferred definitions."""
from __future__ import annotations
from jaclang.runtimelib.builtin import *
from jaclang import JacMachineInterface as _jl
from enum import Enum, auto

@_jl.impl_patch_filename('/home/ninja/jaseci/jac/examples/reference/implementations.jac')
def compute(x: int, y: int) -> int:
    return x + y

class Vehicle(_jl.Obj):
    name: str = 'Car'
    speed: int = 0

    def accelerate(self) -> None:
        self.speed += 10

@_jl.sem('', {'LOW': '', 'MEDIUM': '', 'HIGH': ''})
class Priority(Enum):
    LOW = 1
    MEDIUM = 2
    HIGH = 3

class Calculator(_jl.Obj):
    total: float = 0.0

    @_jl.impl_patch_filename('/home/ninja/jaseci/jac/examples/reference/implementations.jac')
    def add(self, value: float) -> None:
        self.total += value

    @_jl.impl_patch_filename('/home/ninja/jaseci/jac/examples/reference/implementations.jac')
    def subtract(self, value: float) -> None:
        self.total -= value

    @_jl.impl_patch_filename('/home/ninja/jaseci/jac/examples/reference/implementations.jac')
    def multiply(self, value: float) -> None:
        self.total *= value

    @_jl.impl_patch_filename('/home/ninja/jaseci/jac/examples/reference/implementations.jac')
    def get_result(self) -> float:
        return self.total
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
impl_def: decorators? KW_IMPL dotted_name impl_spec? impl_tail
impl_spec: inherited_archs | func_decl | event_clause
impl_tail: enum_block | block_tail

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++.

obj Calculator {
    has total: float = 0.0;
    def add(value: float);
    def subtract(value: float);
    def multiply(value: float);
    def get_result -> float;
}

with entry {
    calc = Calculator();
    calc.add(10.5);
    calc.multiply(2.0);
    calc.subtract(5.0);
    print("Result:", calc.get_result());
}
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;
}

Running the program:

jac run main.jac main.impl.jac

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

  1. Forward declarations establish names without full definitions
  2. Implementation blocks provide the actual functionality
  3. Signatures must match exactly between declaration and implementation
  4. Useful for circular dependencies and code organization
  5. Similar to header/source separation in C/C++
  6. Elements can be referenced after forward declaration, before implementation
  7. All types (functions, objects, enums) support this pattern