Imports
J2 gets by with one keyword and a search path: import
splices another source file into your program, and a package is nothing more than a
directory of source files you can put on that path.
The import statement
import "path" names a source file whose contents become part of the
program, as if written at the point of the import. Imports appear at the top level, one
per line, conventionally at the top of the file.
With geometry.j2:
func area(w, h) = w * h
func perimeter(w, h) = 2 * (w + h)
a neighboring file uses it like this:
import "geometry.j2"
print(area(3, 4)) # 12
print(perimeter(3, 4)) # 14
Because imports are textual, everything the imported file defines is visible: functions, classes, and top-level bindings alike. There are no namespaces and no selective imports; files share one program scope, so name your definitions accordingly.
The search path
An absolute path is used as written. A relative path is tried in three places, in order:
- next to the importing file,
- in the importing file's
lib/subdirectory, - in each directory listed in the
J2_PATHenvironment variable, colon separated.
J2_PATH is how shared code escapes a single project: keep your utilities
in one directory and export it once in your shell profile.
$ J2_PATH=$HOME/j2/lib j2 run report.j2
Transitive imports and cycles
Imported files may import other files, and the same file imported twice, directly or
through a diamond, is included only once. Cycles are therefore harmless: if
a.j2 and b.j2 import each other, the second inclusion is a
no-op and the program proceeds.
Project layout
A working convention for a program that has outgrown one file:
report/
report.j2 entry point: imports, wiring, top-level flow
parse.j2 one concern per file
render.j2
lib/ utilities shared by the files above
tables.j2
The entry point imports its parts; the parts import what they share from
lib/. A "package" you hand to someone else is simply this directory, and
installing it means putting it somewhere on J2_PATH. There is no package
manager, no manifest, and no registry; a directory of readable source is the unit of
distribution.
The standard library needs no imports
The built-in functions and the standard modules (math,
text, json, fs, and the rest) are available in
every file without any import. The Standard Library page
lists all of them. import exists for your code, not the language's.