Imports
Let's unravel the art of importing files and libraries in FatScript! Why? Well, because in this language you can import whenever your heart desires, simply by using a left arrow <-
.
Dot syntax
To use imports with dot syntax, project files and folders should neither start with a digit nor contain symbols.
you can specify any path you like by using literal paths
Named import
To import files, use the .fat
extension for filenames (or no extension at all). However, omit the extension in the import statement. Here's an example:
ref <- filename
if both
x
andx.fat
files exist, the latter takes precedence
For importing files from folders:
ref1 <- folder.filename
ref2 <- folder.subfolder.filename
To import all files from a folder, use the dot-underscore syntax:
lib <- folder._
Please note: only files immediately inside the folder are included using the above syntax. To include files from subfolders, explicitly mention them. Additionally, a "_.fat" file (or "_" file) inside a folder can override the dot-underscore import behavior.
slashes
/
can also be used as an alternative, such asref <- folder/filename
Element access
Once imported, access elements using dot syntax:
ref1.element1
Element extraction
To extract specific elements from a named import or to avoid prepending the module name every time (e.g., lib.foo
), employ destructuring assignment:
{ foo, bar } = lib
Visibility
Named imports are resolved at the global scope, irrespective of where they are declared. This means even if you declare a named import inside a function or a local scope, it will be globally accessible.
Local import
To import within the current scope, use:
_ <- filename
Local imports, unlike named imports, dump the file content directly into the current scope. Thus, an imported method can be invoked as baz(arg)
rather than ref.baz(arg)
.
While local imports are best suited for importing types into the global scope, they should be used with caution when importing library content. Overusing local imports can lead to namespace pollution, which can make it more challenging to follow the code, because it becomes less apparent where the methods come from.
Literal paths
With literal paths, you may use any filename or extension. However, note that those imports are not evaluated during bundling, but at runtime. Here's an example:
ref <- '_folder/2nd-source.other'
You can also use smart texts as literal paths:
base = 'folder'
file = 'source.xyz'
ref <- '{base}/{file}'
Keep in mind that literal paths can make your code more complex, and those imports can only be dynamically resolved, so use them sparingly.
Import deduplication
FatScript utilizes an "import once" strategy with an in-scope flag mechanism, automatically bypassing files that have already been imported.
Pitfalls of import usage
Local imports within method: Importing directly within a method body re-evaluates the import on every invocation, causing memory retention:
myMethod = -> { _ <- lib # potential memory leak ... }
This behavior is not classified as a bug per se, but rather a consequence of design choices in FatScript's garbage collection (GC) system. The GC's optimizations exclude nodes directly derived from source code, allowing them to evade standard mark-and-sweep procedures. As a result, local imports within methods miss out on deduplication, causing their nodes to remain resident until the program's end.
Selective local imports: Using destructuring assignment on local imports discards other members, but the whole import is processed and bound to the extracted member context:
{ foo1 } = { _ <- lib } # lib is loaded and bound to foo1's context ... { foo2 } = { _ <- lib } # lib is loaded again, and bound to foo2
This pattern creates a closure. Using it for the same library results in repeated loads, increasing memory usage. For better efficiency, consider importing the library once at the top level and referencing it directly or using selective imports sparingly.
Best Practices
To avoid memory issues, follow these strategies:
- Move imports to outer scope: Import libraries at a higher level to ensure single evaluation.
- Use named imports: Prefer named imports to reuse code without redundancy.