Skip to content

5. Modules and imports

Once your program is more than one file, you’ll split it into modules and import what you need.

A tiny multi-file project

Create a folder:

my_project/
├── main.incn
└── strings.incn

strings.incn:

pub def shout(s: str) -> str:
    return s.strip().upper()

main.incn:

from strings import shout

def main() -> None:
    println(shout("  hello  "))

Run from my_project/:

incan run main.incn

Visibility: exporting with pub

By default, definitions are module-private (only usable inside the same .incn file). Prefix a declaration with pub to export it so other modules can import it.

Coming from Python?

In Python, most top-level definitions are effectively importable. In Incan, you typically make the intended “public API” of a module explicit with pub.

For more detail on pub (including how it affects model/class fields), see: Models & Classes.

Exporting an alternate name

Use a symbol alias when a module should expose another name for the same callable or type:

# strings.incn
pub def shout(s: str) -> str:
    return s.strip().upper()

pub yell = shout
# main.incn
from strings import yell

def main() -> None:
    println(yell("  hello  "))

yell is an alias of shout, not a copied function body. The target must already be a supported declaration, and a public alias must target a public symbol. For the full contract, see Symbol aliases.

Import styles

Incan supports two styles you can mix:

# Python-style
from strings import shout

# Rust-style
import strings::shout

The reference documents how parent/root paths work:

  • Parent: .. (Python-style) or super:: (Rust-style)
  • Project root: crate

See: Imports and modules (reference).

Module-owned state with static

Sometimes a module does not just export functions and types.
Sometimes it owns long-lived runtime state.

That is what static is for:

This example shares live runtime state across module boundaries with pub static:

counters.incn
pub static hits: int = 0

pub def record_hit() -> None:
    hits += 1
main.incn
from counters import hits, record_hit

def main() -> None:
    record_hit()
    record_hit()
    println(hits)

This prints 2.

Important:

  • static is runtime state, not a compile-time constant
  • imported pub static names refer to the same live storage
  • static always needs an explicit type and initializer

Use const for baked compile-time data (constants).

Try it

  1. Add another function in strings.incn (for example whisper).
  2. Mark it pub and import it into main.incn.
  3. Call both functions.
One possible solution
# strings.incn
pub def shout(s: str) -> str:
    return s.strip().upper()

pub def whisper(s: str) -> str:
    return s.strip().lower()

# main.incn
from strings import shout, whisper

def main() -> None:
    println(shout("  hello  "))    # outputs: HELLO
    println(whisper("  HELLO  "))  # outputs: hello

What to learn next

Next

Back: 4. Control flow

Next chapter: 6. Errors (Result/Option and ?)