Modules

Modules are a simple way of grouping functions and global variables together. Without modules, you’d have to write all your code in a single script file. A module is therefore an additional way of structuring your code by grouping functions by theme in files, enabling code to be reused across several projects.

There are two types of module:

  • Built-in modules supplied directly with LocalSolver. You’ll find a full list of theses modules and their documentation on the Standard library page.

  • User modules. These modules are coded in LSP by you or other users and are placed alongside your other project files.

The module lookup paths

To search for modules, the LocalSolver interpreter will first look at the corresponding file in the lookup paths. LocalSolver will then search in the list of its builtin modules (such as csv, charset, datetime). User modules therefore always have priority over builtin modules.

By default, the lookup paths contains a single folder. It varies depending on how you run LocalSolver:

  • When using LocalSolver from the command line (with the localsolver program), the folder containing the main script supplied from the command line is considered as the lookup path, regardless of the current execution folder. So, when you call the following script localsolver c:\localsolver\examples\toy\toy.lsp, the lookup path is initialized to the folder c:\localsolver\examples\toy\.

  • When using LocalSolver with the studio, the root of your workspace is considered the only lookup path, regardless of the path to the lsp file or dashboard you’re running.

  • When using the modeler API (in Python, C#, Java or C++), the lookup path is initialized to your program’s current execution folder.

Create a module

A module is automatically created for each LSP file loaded by the interpreter. To create a new user module, simply create a new LSP file. The file must be placed in the same folder as your main LSP file. The filename without extension and path will be used as the module name. It is also possible to place your new LSP files in different folders, using compound module names as shown below.

As the file name is used as the module name, it must comply with LSP rules on identifiers: the name must begin with a letter or the underscore character, followed by a list of alphanumeric characters (letters, numbers or underscore). An exception is made for the main program used as an entry point. This is the only LSP file that is not imported using a use directive. Consequently, its name is free.

For instance, in the example tree below, if the $main.lsp file is your program’s entry point, then the foobar.lsp file is a module that can be loaded from your main program under the name foobar:

/
|- $main.lsp
|- foobar.lsp

You can create or load as many modules as you like in LSP, as long as the module name is unique throughout your program.

Import a module

To import a module into your program, simply use the use keyword followed by the module name. The module is then exposed as a global variable, accessible from anywhere in your program.

Use statements should be placed at the top of your program, after any pragma declarations but before the first class or function declarations.

Using the example given above, you can import the foobar module from your $main.lsp file as follows:

use foobar;

function main() {
    println("Greetings from another module:");
    foobar.sayHelloWorld();
}

Rename a module

When you import a module, a global variable with the same name is automatically created in your program. If this name conflicts with one of your variables, or if you find the name too long, you can change the variable name using the keyword as as shown below:

// Declare a new global variable "geo" and initialize it with the module geodata
use geodata as geo;

function input() {
    geo.computeMatrix(...);
}

Import specific functions or classes of a module

It is also possible to import specific functions and classes of a module directly into the current namespace using the following syntax:

use serialize, parse from json;

The previous example imports serialize and parse functions into the current namespace. To use it, you no longer need to prefix the functions with the module name (here json), thus shortening the syntax. serialize or parse can be used in the same way as if you had programmed them in the current module.

Note, however, that functions and classes imported that way behave as if they had remained in their original module. They therefore only have access to their original global variables and have no access to the variables of the module into which they are imported, unless explicitely passed.

Just like the complete module, you can also rename imported functions or classes using the as keyword, as in the example below:

use serializer, parse as parseJson from json;

Modules located in sub-folders

Until now, imported modules were either builtin-modules, or modules located at the root of lookup paths (i.e. the folder containing the main script, the current execution folder or the current workspace, depending on how you call LocalSolver).

But it is also possible to import modules located in subfolders of the lookup paths. To do this, you can create as many sub-folders as you like in your lookup-paths and place your LSP files in them.

To import them, you’ll then need to write a compound module name. A compound module name is a sequence of valid LSP identifiers separated by a dot. Each intermediate identifier represents the name of one of the sub-folders. The last identifier is the name of the LSP file.

For example, if you place a file named ducks.lsp in a birds folder which is itself in an animals folder, like this:

animals/
  |- birds/
    |- ducks.lsp

The full module name will be animals.birds.ducks. Unlike simple module names, modules with compound names must be renamed with the keyword as. It is not possible to import them directly:

use animals.birds.ducks; // will throw an error
use animals.birds.ducks as ducks; // ok

Note that although the syntax animals.birds.ducks seems to indicate that module ducks is a sub-module of birds, this is not the case. There is no notion of sub-modules in LSP. In the example above, there is only one module with a compound name. Keep in mind that each module is independent of the others, regardless of their organization in the computer’s folders.