Skip to content

Packages#

Packages in Jac are directories containing .jac or .py files.

Prefer Absolute Imports

Use absolute imports like import from mypackage.module { X } for clarity and maintainability.


Simple Package (No __init__.jac Required)#

Import directly from a package directory. The __init__.jac file is optional.

package_no_init/

package_no_init/main.jac
import from mylib.math_utils { add, multiply }
import from mylib.string_utils { greet }

with entry {
    print(f"add(3, 5): {add(3, 5)}");
    print(f"multiply(4, 6): {multiply(4, 6)}");
    print(f"greet('World'): {greet('World')}");
}
View on GitHub

package_no_init/mylib/math_utils.jac
def add(a: int, b: int) -> int {
    return a + b;
}

def multiply(a: int, b: int) -> int {
    return a * b;
}
View on GitHub

package_no_init/mylib/string_utils.jac
def greet(name: str) -> str {
    return f"Hello, {name}!";
}

def uppercase(text: str) -> str {
    return text.upper();
}
View on GitHub
package_no_init/
├── main.jac
└── mylib/              # No __init__.jac needed!
    ├── math_utils.jac
    └── string_utils.jac
Output

```

add(3, 5): 8 multiply(4, 6): 24 greet('World'): Hello, World! ```


Package with Re-exports#

Use __init__.jac when you want to create a simplified public API.

package_basic/

package_basic/main.jac
import from mypackage { HELPER_VALUE, helper_func }

with entry {
    print(f"Package from-import - HELPER_VALUE: {HELPER_VALUE}");
    print(f"Package from-import - helper_func(): {helper_func()}");
}
View on GitHub

package_basic/mypackage/__init__.jac
import from .helper { HELPER_VALUE, helper_func }
View on GitHub

package_basic/mypackage/helper.jac
glob HELPER_VALUE = "Helper value from package";

def helper_func -> str {
    return "Helper function result";
}
View on GitHub
package_basic/
├── main.jac
└── mypackage/
    ├── __init__.jac    # Re-exports from helper.jac
    └── helper.jac
Output

```

Package from-import - HELPER_VALUE: Helper value from package Package from-import - helper_func(): Helper function result ```


Nested Package Imports#

Access deeply nested packages using dot notation.

nested_packages/

nested_packages/main.jac
import from app.services.user_service { create_user, get_version }
import from app.models.user { User }
import from app.constants { APP_NAME }

with entry {
    print(f"Nested deep - APP_NAME: {APP_NAME}");
    print(f"Nested deep - get_version(): {get_version()}");
    user = create_user("Alice");
    print(f"Nested deep - create_user('Alice'): {user}");
}
View on GitHub

nested_packages/app/__init__.jac
import from .constants { APP_NAME, VERSION }
View on GitHub

nested_packages/app/constants.jac
glob APP_NAME = "MyApp";
glob VERSION = "1.0.0";
View on GitHub

nested_packages/app/models/user.jac
import from ..constants { APP_NAME }

obj User {
    has name: str;
    has app: str = APP_NAME;

    def __repr__ -> str {
        return f"User({self.name}) from {self.app}";
    }
}
View on GitHub

nested_packages/app/services/user_service.jac
import from ..models.user { User }
import from ..constants { VERSION }

def create_user(name: str) -> User {
    return User(name=name);
}

def get_version -> str {
    return VERSION;
}
View on GitHub
nested_packages/
├── main.jac
└── app/
    ├── __init__.jac
    ├── constants.jac
    ├── models/
    │   ├── __init__.jac
    │   └── user.jac
    └── services/
        ├── __init__.jac
        └── user_service.jac
Output

```

Nested deep - APP_NAME: MyApp Nested deep - get_version(): 1.0.0 Nested deep - create_user('Alice'): User(Alice) from MyApp ```


Re-exports with __init__.jac#

While optional, __init__.jac can be used to create a clean public API by re-exporting symbols.

init_reexport/

init_reexport/main.jac
import from mathlib { add, subtract, multiply, divide, PI, E, Calculator }

with entry {
    print(f"Init reexport - add(5, 3): {add(5, 3)}");
    print(f"Init reexport - subtract(10, 4): {subtract(10, 4)}");
    print(f"Init reexport - multiply(6, 7): {multiply(6, 7)}");
    print(f"Init reexport - divide(20, 4): {divide(20, 4)}");
    print(f"Init reexport - PI: {PI}");
    print(f"Init reexport - E: {E}");
    calc = Calculator();
    print(
        f"Init reexport - Calculator.compute(10, 2, 'add'): {calc.compute(10, 2, 'add')}"
    );
}
View on GitHub

init_reexport/mathlib/__init__.jac
import from .operations { add, subtract, multiply, divide }

import from .constants { PI, E, GOLDEN_RATIO }

import from .calculator { Calculator }
View on GitHub

init_reexport/mathlib/operations.jac
def add(a: float, b: float) -> float {
    return a + b;
}

def subtract(a: float, b: float) -> float {
    return a - b;
}

def multiply(a: float, b: float) -> float {
    return a * b;
}

def divide(a: float, b: float) -> float {
    if b == 0 {
        return 0.0;
    }
    return a / b;
}
View on GitHub

init_reexport/mathlib/constants.jac
glob PI = 3.14159;
glob E = 2.71828;
glob GOLDEN_RATIO = 1.61803;
View on GitHub

init_reexport/mathlib/calculator.jac
import from .operations { add, subtract, multiply, divide }
import from .constants { PI }

obj Calculator {
    has last_result: float = 0.0;

    def compute(a: float, b: float, operation: str) -> float {
        if operation == "add" {
            self.last_result = add(a, b);
        } elif operation == "subtract" {
            self.last_result = subtract(a, b);
        } elif operation == "multiply" {
            self.last_result = multiply(a, b);
        } elif operation == "divide" {
            self.last_result = divide(a, b);
        }
        return self.last_result;
    }

    def get_pi -> float {
        return PI;
    }
}
View on GitHub
init_reexport/
├── main.jac
└── mathlib/
    ├── __init__.jac      # Re-exports from submodules
    ├── calculator.jac
    ├── constants.jac
    └── operations.jac
Output

```

Init reexport - add(5, 3): 8 Init reexport - subtract(10, 4): 6 Init reexport - multiply(6, 7): 42 Init reexport - divide(20, 4): 5.0 Init reexport - PI: 3.14159 Init reexport - E: 2.71828 Init reexport - Calculator.compute(10, 2, 'add'): 12 ```

When to use __init__.jac

Use __init__.jac when you want to:

  • Create a simplified public API
  • Hide internal module structure
  • Add package-level constants or initialization

Sibling Subpackage Imports#

Import between sibling subpackages using absolute paths.

sibling_subpackage/

sibling_subpackage/main.jac
import from top.sub_a.a_module { A_VALUE, a_func }
import from top.sub_b.b_module { B_VALUE, b_func }

with entry {
    print(f"Sibling subpkg - A_VALUE: {A_VALUE}");
    print(f"Sibling subpkg - a_func(): {a_func()}");
    print(f"Sibling subpkg - B_VALUE: {B_VALUE}");
    print(f"Sibling subpkg - b_func(): {b_func()}");
}
View on GitHub

sibling_subpackage/top/sub_a/a_module.jac
glob A_VALUE = "A module value";

def a_func -> str {
    return "A function";
}
View on GitHub

sibling_subpackage/top/sub_b/b_module.jac
import from ..sub_a.a_module { A_VALUE, a_func }

glob B_VALUE = f"B uses {A_VALUE}";

def b_func -> str {
    return f"B calls: {a_func()}";
}
View on GitHub
sibling_subpackage/
├── main.jac
└── top/
    ├── __init__.jac
    ├── sub_a/
    │   ├── __init__.jac
    │   └── a_module.jac
    └── sub_b/
        ├── __init__.jac
        └── b_module.jac    # Can import from sub_a
Output

```

Sibling subpkg - A_VALUE: A module value Sibling subpkg - a_func(): A function Sibling subpkg - B_VALUE: B uses A module value Sibling subpkg - b_func(): B calls: A function ```


Key Takeaways#

Concept Description
Packages Directories with .jac or .py files
__init__.jac Optional, useful for re-exports
Absolute imports import from pkg.subpkg.module { X }
Nested access Use dot notation for deep packages

Best Practice

Use absolute imports with full package paths: import from app.models.user { User }. This is explicit and avoids ambiguity.