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.
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')}");
}
def add(a: int, b: int) -> int {
return a + b;
}
def multiply(a: int, b: int) -> int {
return a * b;
}
def greet(name: str) -> str {
return f"Hello, {name}!";
}
def uppercase(text: str) -> str {
return text.upper();
}
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.
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()}");
}
glob HELPER_VALUE = "Helper value from package";
def helper_func -> str {
return "Helper function result";
}
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.
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}");
}
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}";
}
}
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;
}
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.
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')}"
);
}
import from .operations { add, subtract, multiply, divide }
import from .constants { PI, E, GOLDEN_RATIO }
import from .calculator { Calculator }
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;
}
glob PI = 3.14159;
glob E = 2.71828;
glob GOLDEN_RATIO = 1.61803;
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;
}
}
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.
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()}");
}
glob A_VALUE = "A module value";
def a_func -> str {
return "A function";
}
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()}";
}
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.