# How Nelson's Module System Works and How to Create a Custom Module

> Discover how Nelson's module system uses ModulesManager and simple scripts like loader.m to manage functionality. Learn to create your own custom module easily.

- Repository: [The Nelson Programming Language/nelson](https://github.com/nelson-lang/nelson)
- Tags: deep-dive
- Published: 2026-03-08

---

**Nelson's module system uses a C++ `ModulesManager` to register directories under `modules/`, exposing functionality through built-ins like `addmodule` and `modulepath`, while each module uses `loader.m`, `startup.m`, and `finish.m` scripts to initialize and clean up resources.**

Nelson organizes its functionality into discrete units called *modules*. Each module lives in its own directory under the `modules/` folder and registers with the runtime through a small amount of boilerplate code. Understanding how Nelson's module system works is essential for extending the language with custom functionality or packaging existing code for redistribution.

## Core Architecture of Nelson's Module System

### ModulesManager and C++ Registration Layer

At the heart of Nelson's module system lies the `ModulesManager` class implemented in [`modules/modules_manager/src/cpp/ModulesManager.cpp`](https://github.com/nelson-lang/nelson/blob/main/modules/modules_manager/src/cpp/ModulesManager.cpp). This singleton maintains a vector of registered modules, storing metadata including `modulename`, `modulepath`, `isprotected`, version, and library path.

When you register a module, the manager inserts a new entry via `RegisterModule`:

```cpp
modulesMap.emplace_back(modulename, path, L"", protectedModule, version);

```

The manager provides APIs such as `UnregisterModule`, `IsExistingModuleName`, `IsProtectedModuleName`, and `GetModules` to query and manipulate the module registry at runtime.

### Built-in Functions for Module Management

The C++ layer exposes functionality to the Nelson language through several built-in functions:

- **`addmodule`** ([`modules/modules_manager/builtin/cpp/addmoduleBuiltin.cpp`](https://github.com/nelson-lang/nelson/blob/main/modules/modules_manager/builtin/cpp/addmoduleBuiltin.cpp)): Inserts a new entry into `ModulesManager` by calling `RegisterModule`.
- **`ismodule`** ([`modules/modules_manager/builtin/cpp/ismoduleBuiltin.cpp`](https://github.com/nelson-lang/nelson/blob/main/modules/modules_manager/builtin/cpp/ismoduleBuiltin.cpp)): Returns a logical value indicating whether a short name refers to a loaded or protected module.
- **`modulepath`** ([`modules/modules_manager/builtin/cpp/modulepathBuiltin.cpp`](https://github.com/nelson-lang/nelson/blob/main/modules/modules_manager/builtin/cpp/modulepathBuiltin.cpp)): Returns absolute paths to module subdirectories. Accepts options: `'etc'`, `'bin'`, `'root'`, `'builtin'`, `'functions'`, or `'tests'`.

## Module Lifecycle and Initialization

### loader.m: Registration

Every module contains a `loader.m` file in its root directory (e.g., `modules/xml/loader.m`). This script executes automatically at startup and calls `addmodule` to register the module with the runtime:

```matlab
% modules/my_module/loader.m
addmodule([nelsonroot() '/modules/' 'my_module'], 'my_module');

```

This invocation stores the module metadata in the `ModulesManager` vector, making the module discoverable by `ismodule` and `modulepath`.

### startup.m: Initialization

After registration, Nelson executes `etc/startup.m` within the module directory. This script typically performs two critical tasks:

1. **Register the gateway**: Calls `addgateway` to map function calls to the module's compiled library.
2. **Add functions to path**: Calls `addpath` with the `-frozen` flag to include the module's `functions/` directory.

Example from `modules/xml/etc/startup.m`:

```matlab
% modules/my_module/etc/startup.m
% Register the built-in library gateway
addgateway(modulepath('my_module','builtin'), 'my_module');

% Add functions directory to path (frozen prevents overwriting)
addpath(modulepath('my_module','functions'), '-frozen');

```

### finish.m: Cleanup

When Nelson shuts down or the module unloads, it executes `etc/finish.m` to reverse the initialization steps:

```matlab
% modules/my_module/etc/finish.m
% Remove functions from path
rmpath(modulepath('my_module','functions'));

% Remove the gateway
removegateway(modulepath('my_module','builtin'));

```

## How to Create a Custom Module in Nelson

Creating a custom module involves creating a directory structure and three mandatory scripts. Below is a complete, runnable example.

### Step 1: Create the Directory Structure

Create the following hierarchy under `modules/my_module`:

```

modules/
└─ my_module/
   ├─ loader.m
   ├─ etc/
   │  ├─ startup.m
   │  └─ finish.m
   ├─ functions/
   │   └─ hello.m
   └─ tests/
       └─ test_hello.m

```

### Step 2: Write loader.m

This file registers your module with the `ModulesManager`:

```matlab
% modules/my_module/loader.m
addmodule([nelsonroot() '/modules/' 'my_module'], 'my_module');

```

### Step 3: Configure startup.m and finish.m

**startup.m** initializes the module:

```matlab
% modules/my_module/etc/startup.m
% Register gateway for built-in functions (if you have a .dll/.so)
addgateway(modulepath('my_module','builtin'), 'my_module');

% Add functions directory to Nelson path
addpath(modulepath('my_module','functions'), '-frozen');

```

**finish.m** cleans up:

```matlab
% modules/my_module/etc/finish.m
rmpath(modulepath('my_module','functions'));
removegateway(modulepath('my_module','builtin'));

```

### Step 4: Add Functions and Tests

Create a simple function in `functions/hello.m`:

```matlab
% modules/my_module/functions/hello.m
function out = hello(name)
%HELLO  Simple greeting example
%   out = hello('Nelson')  returns  "Hello, Nelson!"
    if nargin == 0
        name = 'World';
    end
    out = sprintf('Hello, %s!', name);
end

```

Optional test in `tests/test_hello.m`:

```matlab
% modules/my_module/tests/test_hello.m
function test_hello
    assert_isequal(hello('Nel'), 'Hello, Nel!');
end

```

### Step 5: Load and Verify

You can manually load your module in a Nelson session:

```matlab
>> addmodule([nelsonroot() '/modules/my_module'], 'my_module');
>> hello('Nelson')
ans =  "Hello, Nelson!"

```

Alternatively, restart Nelson. The **Modules Manager** automatically executes every `loader.m` found under `modules/` during startup.

## Using Module Management Built-ins

Nelson provides several built-in functions to query and manage modules at runtime:

Check if a module is loaded:

```matlab
>> ismodule('my_module')
ans = true

```

Retrieve specific paths within a module:

```matlab
>> modulepath('my_module','etc')
ans = "/path/to/nelson/modules/my_module/etc"

>> modulepath('my_module','functions')
ans = "/path/to/nelson/modules/my_module/functions"

```

List all loaded modules with metadata:

```matlab
>> mods = getmodules()
mods = 
  struct with fields:
    name: {'my_module'  'xml'  ...}
    path: {"/path/to/..."  ...}
    protected: [0  1  ...]
    version: {'1.0.0'  ...}

```

## Summary

- **Nelson's module system** is managed by a C++ `ModulesManager` class that maintains a registry of all loaded modules, storing metadata such as name, path, version, and protection status.
- **Registration** occurs through `loader.m` scripts that call the `addmodule` built-in, which invokes `RegisterModule` in [`modules/modules_manager/src/cpp/ModulesManager.cpp`](https://github.com/nelson-lang/nelson/blob/main/modules/modules_manager/src/cpp/ModulesManager.cpp).
- **Initialization** happens via `etc/startup.m`, which typically calls `addgateway` to link compiled libraries and `addpath` to expose `.m` functions.
- **Cleanup** is handled by `etc/finish.m` using `rmpath` and `removegateway`.
- **Query functions** like `ismodule`, `modulepath`, and `getmodules` provide runtime introspection into the module registry.
- **Custom modules** require a specific directory layout (`loader.m`, `etc/startup.m`, `etc/finish.m`, `functions/`) and can be auto-loaded at startup or manually registered via `addmodule`.

## Frequently Asked Questions

### What is the difference between `addmodule` and `addpath` in Nelson?

`addmodule` registers a directory as a formal module within the C++ `ModulesManager`, creating a persistent entry with metadata (name, path, version, protection status) that survives for the session and can be queried via `ismodule` and `getmodules`. In contrast, `addpath` simply adds a directory to Nelson's function search path for `.m` files; it does not create a module entry, support versioning, or trigger `startup.m`/`finish.m` lifecycle scripts.

### How do I protect a custom module from being unloaded?

Protected status is determined by the `isprotected` flag stored in the `ModulesManager` vector (see [`modules/modules_manager/src/cpp/ModulesManager.cpp`](https://github.com/nelson-lang/nelson/blob/main/modules/modules_manager/src/cpp/ModulesManager.cpp)). When calling `addmodule`, you cannot directly set this flag from Nelson script—it is typically reserved for core modules distributed with Nelson itself. To prevent accidental modification of your custom module's functions, use the `-frozen` flag when calling `addpath` in your `startup.m` file: `addpath(modulepath('my_module','functions'), '-frozen')`. This prevents user paths from shadowing your module's functions.

### Can I distribute a Nelson module as a standalone package?

Yes. A Nelson module is self-contained within its directory structure (`loader.m`, `etc/`, `functions/`, etc.). You can distribute this directory as a zip file or Git repository. Users simply place it under their `modules/` directory, and Nelson's `ModulesManager` will automatically execute `loader.m` at startup, registering the module. For convenience, Nelson provides a skeleton generator via `just/get_module_skeleton.just`, which clones the official template repository to help you package your module correctly with all required boilerplate files.

### Where does Nelson store module version information?

Version metadata is read from each module's [`module.json`](https://github.com/nelson-lang/nelson/blob/main/module.json) file via the `readVersionFromJson` function inside [`modules/modules_manager/src/cpp/ModulesManager.cpp`](https://github.com/nelson-lang/nelson/blob/main/modules/modules_manager/src/cpp/ModulesManager.cpp). When `addmodule` registers a module, the `ModulesManager` stores the version string alongside the module name, path, and protection status in its internal vector. You can inspect the version field for all loaded modules by calling `getmodules()`, which returns a struct array containing the `version` field for each registered module.