Modules
File-based modules with pub visibility.
File = Module
Loon's module system holds one idea and no more: every .loon file is a module, and its name is the file name with the extension dropped. No module declarations, no manifests, no boilerplate. Write a file called greet.loon and you have a module called greet.
LOON
; greet.loon
[fn hello [] "hi"]
[pub fn world [] "world"]Everything in a module is private until you say otherwise. The hello function above is invisible from outside; only world, marked pub, is reachable by other modules. Encapsulation costs you nothing, and your internal helpers stay internal.
Pub Exports
To let something out of its module, write pub before the definition. It works on functions, bindings, and types alike.
LOON
[pub fn add [a b] [+ a b]]
[pub let version "0.1.0"]
[pub type Color
Red Green Blue]pub is the front door of your module. Everything behind it is an implementation detail you are free to change without breaking anyone. The pub items, and only those, are your module's contract with the world.
Use / Import
To bring a module into scope, reach for the use keyword. Once imported, you call its exports through dot syntax.
LOON
[use math]
[println [math.add 1 2]]The module name is the file name, so [use math] looks for math.loon. Predictable, and self-explaining: there is no configuration file to comb through to learn where a module lives.
Aliasing
Module names are sometimes long, or they collide with something already in scope. Rename one on import with as.
LOON
[use string-utils as str-u]
[println [str-u.capitalize "hello"]]Once aliased, the original name is gone from scope; only the alias answers. The result stays unambiguous, and you get to choose the name that reads best where you are.
Selective Imports
When you need only a few names from a module, pull them straight into scope. No dot prefix required.
LOON
[use math [add subtract]]
[println [add 1 2]]This earns its place when you lean on a function often and the module.function spelling starts to drag. Take care not to import names that collide with your own.
Directory Structure
As a project grows, you will reach for directories to keep modules in order. Nesting builds module paths, the segments joined by /.
STRUCTURE
src/
main.loon
math.loon
utils/
string.loon
io.loonLOON
[use utils/string]
[println [string.trim " hi "]]Note the split: you import by the full path (utils/string) but refer to the module by its last segment (string). Use stays short, while the import remains explicit about where the module came from.
Note
A directory with a mod.loon file exports that file as the directory module, so utils/mod.loon lets you write [use utils] directly.
Cycle Detection
Circular dependencies breed confusion and quiet bugs. Loon catches them at compile time and points to exactly where the cycle closes.
ERROR
error: circular dependency detected
--> a.loon:1
a.loon -> b.loon -> a.loonThe fix is usually clean: lift the shared code into a third module that both files import. A cycle is a sign that two modules are more entangled than they ought to be, so breaking it tends to leave the design better than it found it.