Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Modules and imports

A module groups related values. Import statements belong at module scope. An imported name is the final component of its path.

import "json"
import "./modules/greeter"

print(json.marshal({"ok": true}))
greeter.greet("world")

Local modules

Local paths must start with ./ or ../ and are resolved relative to the file that imports them; any other path, slashes included, names a standard-library module (like "x/compress/gzip"). Omit the .goblin suffix. A local module chooses its public names with export.

# modules/greeter.goblin
var greeting = "Hello"

func greet(name) {
    print(greeting, name)
}

export greet

Names without export remain private. Define a module-level name before code that uses it; this also applies to names referenced from an exported function. Named functions may call themselves recursively.

A complete local module

For a small project, keep the entry point and local module in separate files:

project/
├── main.goblin
└── modules/
    └── greeter.goblin
# modules/greeter.goblin
var greeting = "Hello"

func greet(name) {
    return greeting + ", " + name
}

export greet
# main.goblin
import "./modules/greeter"

print(greeter.greet("world"))

Run the entry point from project/ with goblin run main.goblin. Local import paths are resolved from the importing file, not from the shell's current directory. A local import's final path component becomes its module name, so ./modules/greeter is available as greeter.

A module is loaded once and cached; later imports of the same file reuse the loaded module. Two modules importing each other is a cycle, reported as circular import detected.

The remaining chapters document each built-in module separately.