Module system
SCL organises code into modules. Each module is a separate compilation unit that can
export definitions for use by other modules.
Creating a module
Modules are created in the Simantics Model Browser:
- Right-click the desired library node in the Model Browser.
- Select New → SCL Module.
- Enter the module name. The name becomes the module's path (e.g.
MyLib/Utils).
The module opens in the SCL Editor View for editing. Save with Ctrl+S; the
SCL Issues View shows any compilation errors immediately.
A module may begin with a header that declares its name and optional features:
module {
features = [edo, fields]
}
The features line is only needed when using advanced syntax extensions. The feature
flags are:
edo - enables the edo monadic block syntax — see 1.15 Functors and monads.
fields - enables the . record type field addressing syntax - see 1.13 Data types.
chr - enables ruleset declarations and switches select to the CHR query engine,
at the cost of the rule and transformation declarations — see
4.02 Constraint Handling Rules. Note that ordinary CHR rules need no feature flag.
A module header can only appear in a module. SCL scripts and the SCL Console have no
header, and therefore none of these features — see 1.18 Modules vs. scripts.
Importing modules
import makes the exported symbols of another module visible in the current module under
their original names:
import "Prelude"
import "Simantics/DB"
import "Apros/Legacy"
Symbols from imported modules are accessible directly by name. If two modules export the
same name, the later import wins; use qualified imports (see below) to resolve clashes.
Qualified import — import under a local alias, requiring the alias as a prefix:
import "Map" as Map
import "Set" as Set
m = Map.fromList [("a", 1)]
s = Set.fromList [1, 2, 3]
Restricted import — import only specific names:
import "Prelude" (map, filter, foldl)
Hiding import — import everything except specific names:
import "Prelude" hiding (sort)
Including modules
include makes another module's exports part of the current module's public interface —
effectively re-exporting them:
include "MyLib/CommonDefs"
Use include to build aggregate modules that gather related definitions from several
sub-modules. Consumers of the including module see all included symbols without needing
to import each sub-module separately.
Import vs include
|
import |
include |
| Makes symbols locally visible |
Yes |
Yes |
| Re-exports symbols to importers |
No |
Yes |
| Typical use |
Using another module's functions |
Building aggregate / facade modules |
Cyclic imports
Cyclic imports are not allowed. If module A imports module B and module B imports
module A (directly or transitively), the compiler reports an error. Break the cycle by
extracting shared definitions into a third module that both A and B can import.
The Prelude
The Builtin and Prelude modules are imported automatically into every SCL module.
Prelude provides the standard library functions (map, filter, foldl, arithmetic
operators, show, print, etc.). You do not need to write import "Prelude"
explicitly.
Note that a module gets Prelude only, while SCL scripts and the SCL Console get the
much larger StandardLibrary instead. Code that runs unchanged in the console may need
extra imports once it is moved into a module — see 1.18 Modules vs. scripts.
Module path conventions
Module paths use / as a separator and typically follow a reversed domain / hierarchical
convention:
Simantics/DB // platform core database access
Apros/Legacy // Apros legacy scripting API
MyProject/Analysis // user module in "MyProject"
The path is also the string you pass to import.
|