Modules

Parsley modules are .pars files. Any file can import another, and only exported values are visible to the importer. Non-exported values remain private. Modules are evaluated once and cached — subsequent imports return the same dictionary.

Importing

import is an expression that returns a dictionary of a module's exports. There are three ways to use it:

Namespace Import

Import the whole module as a named dictionary. The name is derived from the path automatically:

import @std/math
math.floor(3.7)                  // 3
math.ceil(3.2)                   // 4

Aliased Import

Rename the module with as:

import @std/math as M
M.floor(3.7)                    // 3

Destructured Import

Pull out specific exports with let destructuring:

let {floor, ceil} = import @std/math
floor(3.7)                       // 3
ceil(3.2)                        // 4

Import Paths

Path type Syntax Resolves to
Standard library @std/math Built-in stdlib module
Relative @./utils.pars Relative to current file
Project root @~/lib/utils.pars Relative to project root

All import paths start with @. The prefix determines how the path is resolved:

import @std/math                 // stdlib
import @./helpers.pars           // sibling file
import @./lib/format.pars        // subdirectory
import @~/shared/config.pars     // project root

Exporting

Use export to make values available to importers. Everything else in the file is private.

export let

Declare and export in one statement:

export let greeting = "Hello"
export let double = fn(x) { x * 2 }

export assignment

Export a value by name:

export PI = 3.14159
export square = fn(x) { x * x }

Bare export

Export a value that was already defined:

let helper = fn(x) { x + 1 }
export helper

Destructured export

Export multiple values from a destructuring assignment:

export {width, height} = getDimensions()

Computed Exports

export computed creates an export that recalculates on every access. Useful for exposing live data like database queries or timestamps:

export computed timestamp = @now
export computed count = items.length()

Block form for multi-line computations:

export computed activeUsers {
    let query = "SELECT * FROM users WHERE active = true"
    @DB.query(query)
}

Computed exports look like regular values to the consumer, but each access re-evaluates the body:

import @./data.pars

// Each access runs the query again
for (user in data.activeUsers) { user.name }  // Query 1
for (user in data.activeUsers) { user.email } // Query 2

// Snapshot by assigning to a variable
let snapshot = data.activeUsers               // Query 3
for (user in snapshot) { user.name }          // Uses snapshot
for (user in snapshot) { user.email }         // Uses snapshot

⚠️ Computed exports recalculate on every access. If the computation is expensive, assign the result to a local variable to avoid redundant work.

Module Example

A module file (mathutils.pars):

// Private — not visible to importers
let square = fn(x) { x * x }

// Public API
export PI = 3.14159
export pythagoras = fn(a, b) {
    math.sqrt(square(a) + square(b))
}
export cube = fn(x) { x * x * x }

Consuming the module:

import @./mathutils.pars
mathutils.PI                     // 3.14159
mathutils.cube(3)                // 27

// Or destructure what you need
let {PI, pythagoras} = import @./mathutils.pars
pythagoras(3, 4)                 // 5

Module Scope

Each module has its own isolated environment:

Caching

Modules are evaluated once. The first import runs the file and caches the resulting dictionary. All subsequent imports of the same path return the cached result, regardless of where the import appears:

// Both get the same cached module dictionary
import @./config.pars
let {theme} = import @./config.pars    // no re-evaluation

Circular Import Prevention

If module A imports module B and module B imports module A, Parsley detects the cycle and raises an import error. Restructure shared code into a third module that both can import.

Standard Library Modules

Module Description
@std/math Mathematical functions (floor, ceil, sqrt, abs, etc.)
@std/valid Validation helpers
@std/id ID generation (UUID, nanoid, etc.)
@std/table Table constructor (deprecated — prefer @table literal)
@std/api@basil/api API utilities (deprecated path)
@std/mdDoc Markdown document processing
@std/dev@basil/log Development/debugging tools (deprecated path)
@std/html@basil/html HTML utilities (deprecated path)
@std/schema Schema utilities (deprecated — prefer @schema DSL)

Key Differences from Other Languages

See Also