Skip to content

Overhaul import loading semantics - #203

Open
rTreutlein wants to merge 21 commits into
mainfrom
import-overhaul
Open

rTreutlein wants to merge 21 commits into
mainfrom
import-overhaul

Conversation

@rTreutlein

Copy link
Copy Markdown
Collaborator

Reworks import! around runtime, in-order resolution. Replaces #201 with a cleaner approach after review discussion.

Changes

  • Relative imports resolve from the importing file. load_metta_file maintains a working-directory stack (exception-safe via setup_call_cleanup), so a file imported from another directory finds its own relative MeTTa and Python dependencies. Previously a single global working_dir was set once at startup.
  • Import errors surface instead of failing silently. A missing import target throws existence_error; syntax errors and Python import failures in dependencies propagate wrapped with the offending filename. Because a throwing import! no longer backtracks, library/2 now prefers the candidate path whose source file exists across all registered library paths.
  • Imports are idempotent and cycle-safe. Per-space load state keyed by canonical path: repeated imports are no-ops, mutual imports terminate, and a failed load clears its state so the file can be fixed and re-imported in the same session (covered by a pytest).
  • Git import failures surface. git-import! (unchanged location, lib_import.pl) now checks clone/build exit status and throws process_error instead of discarding output; repository paths register canonicalized.
  • Warning on use-before-import. A function registered after its name was already compiled as a plain symbol prints a warning telling the user to move the import or definition above the first use — replacing silent non-reduction, the sharpest edge of the old behavior. Import targets are resolved strictly at runtime, in file order, so runtime-generated or computed import paths keep working.

Testing

  • test.sh: all 149 examples pass, no spurious warnings across the corpus (new examples cover relative-import nesting, error surfacing, duplicate/cycle imports, and per-space import identity)
  • python/tests: 5 pass, including new tests for retry-after-failed-import and the use-before-import warning

🤖 Generated with Claude Code

rTreutlein and others added 5 commits July 21, 2026 10:39
Imports used to resolve against a single global working_dir set once at
startup, so a file imported from another directory could not find its own
relative dependencies. load_metta_file now maintains a stack of working
directories (pushed on entry, popped via setup_call_cleanup), making both
MeTTa and Python imports resolve relative to the file that declares them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
import! previously swallowed every failure via catch(_, fail), so a missing
file, a syntax error in a dependency, or a failing Python module import all
passed silently. Imports now resolve their target to a canonical path and
throw existence_error when it is missing; errors from loading a dependency
propagate wrapped with the offending filename. Since a throwing import! no
longer fails into backtracking, library/2 now prefers the candidate whose
source file actually exists instead of relying on backtracking across
registered library paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Importing the same file twice into the same space re-asserted all of its
clauses, and two files importing each other recursed until the stack blew.
import! now tracks per-space load state keyed by the canonical file path:
a loaded entry turns repeated imports into no-ops, a loading entry breaks
cycles, and failed loads clear their entry so the source can be repaired
and the import retried within the same session.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
git-import! piped clone and build output into discarded strings and never
checked the exit status, so a failed clone or build step passed silently
and left the import broken. Both steps now inherit the caller's stdio and
throw process_error on a nonzero exit. The repository path is registered
canonicalized and only once, also removing a stray debug print.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Definitions compile expression heads that are not registered functions
into plain symbols, so a function imported or defined after its first use
is silently never called by the already-compiled expressions. The
translator now records atoms it compiles as symbol heads, and registering
a function whose name was already compiled that way prints a warning
telling the user to move the import or definition above the first use.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MesTTo and others added 3 commits July 22, 2026 22:21
…dent imports

Fix issue #181 on top of PR #203. A definition referencing a not-yet-registered
function compiles the reference as inert data, so cross-file meaning depended on
import order. Scanning imports upfront cannot work: files fetched by git-import!
or generated at runtime are invisible to a scan on a fresh machine but visible
once a previous run created them, making program behavior depend on leftover
on-disk state. Instead, when a function name registers after stored definitions
already compiled it as plain data, those definitions are recompiled from their
source terms. Definitions then mean the same thing on every machine regardless
of import order, including for libraries that only become loadable at runtime.
Expressions that already executed cannot be recompiled retroactively and keep
the warning.
A (git-dependency url rev [build [basedir]]) form declares a repository the
file needs at an exact commit. Declarations are collected after parsing and
satisfied before any form runs, the way cargo fetches its locked dependency
graph before compiling: checkouts are staged, verified against the pinned
commit, retargeted when the pin changes, and refused when locally modified,
so a fresh clone and a machine with a leftover checkout behave identically.
A checkout may declare its own dependencies in deps.metta at its repository
root, which are acquired transitively; the same url pinned to two commits in
one run is an error. The machinery lives in src/gitimport.pl; the runnable
git-import! in lib_import.pl remains for dynamic acquisition.
@patham9

patham9 commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator
!(git-import! "https://github.com/patham9/mettaclaw.git")

currently leads to

ERROR: [Thread main] /home/nartech/PeTTa/src/main.pl:34: user:main 'git-import!': Domain error: `git_origin' expected, found `'https://github.com/patham9/mettaclaw'' (expected(https://github.com/patham9/mettaclaw.git))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants