Skip to content

Latest commit

 

History

History
executable file
·
438 lines (335 loc) · 7.7 KB

File metadata and controls

executable file
·
438 lines (335 loc) · 7.7 KB

Modules

Nari supports modular code organization through the import statement.

Import Statement

Basic Import

Import functions and variables from another file:

// math_utils.nari
func add(a, b) {
    return a + b;
}

func multiply(a, b) {
    return a * b;
}

global PI = 3.14159;
// main.nari
import "math_utils.nari";

print(add(5, 3));        // 8
print(multiply(4, 2));   // 8
print(PI);               // 3.14159

Relative Paths

// Import from same directory
import "helpers.nari";

// Import from subdirectory
import "utils/math.nari";

// Import from parent directory  
import "../shared.nari";

Absolute Paths

import "/usr/local/lib/nari/stdlib.nari";

Module Organization

Recommended Structure

project/
  +-- main.nari
  +-- lib/
  |   +-- utils.nari
  |   +-- helpers.nari
  |   +-- constants.nari
  +-- modules/
      +-- user.nari
      +-- database.nari

Module Pattern

Create modules with encapsulated functionality:

// user_module.nari

global UserModule = {
    create: func(name, email) {
        return {
            name: name,
            email: email,
            createdAt: 0  // timestamp
        };
    },
    
    validate: func(user) {
        if (!user.name || user.name.length() < 3) {
            return false;
        }
        if (user.email.index_of("@") == -1) {
            return false;
        }
        return true;
    },
    
    to_string: func(user) {
        return user.name @ " <" @ user.email @ ">";
    }
};
// main.nari
import "user_module.nari";

let user = UserModule.create("Alice", "alice@example.com");
if (UserModule.validate(user)) {
    print(UserModule.to_string(user));
}

Global Variables Across Modules

Defining Globals

// config.nari
global APP_NAME = "MyApp";
global APP_VERSION = "1.0.0";
global DEBUG = true;
// main.nari
import "config.nari";

print(APP_NAME @ " v" @ APP_VERSION);
if (DEBUG) {
    print("Debug mode enabled");
}

Module-Level Initialization

// database.nari

global connection = null;

func __init__() {
    // This runs when module is imported
    connection = { 
        host: "localhost",
        port: 5432,
        connected: false
    };
    print("Database module initialized");
}

// Call initialization
__init__();

global DB = {
    connect: func() {
        connection.connected = true;
        print("Connected to database");
    },
    
    disconnect: func() {
        connection.connected = false;
        print("Disconnected");
    }
};

Import Behavior

Import Execution

  • Imported files are executed once when first imported
  • Subsequent imports of the same file are ignored (import caching)
  • Circular imports are prevented
// lib.nari
print("Lib module loaded");  // Printed once

func helper() {
    return 42;
}
// main.nari
import "lib.nari";  // Prints: "Lib module loaded"
import "lib.nari";  // No output (already loaded)

print(helper());

Import Order

Imports are processed in order:

import "first.nari";   // Executes completely
import "second.nari";  // Then this executes
import "third.nari";   // Finally this

// All imports are complete here

Standard Library

The standard library is automatically imported:

// No import needed for these globals:
// - math, fs, http, net, JSON, Object, Array, String
// - platform, process, Spawn, yield, stdlib_version
// - Ok, Err, Some, None

print(math.sqrt(16));        // 4
print(stdlib_version);       // "0.0.4"

There is no system module and no io module. Standard input lives on process.stdin, and file operations live on fs.

These names are already bound, so do not reuse them. A top-level func process(x) { ... } is silently ignored because the process global wins, and the same declaration nested inside another function is a parse error. Pick a different name.

See Standard Library for details.

Code Organization Examples

Utilities Module

// utils/string_utils.nari

global StringUtils = {
    capitalize: func(str) {
        if (str.length() == 0) return str;
        let first = str.char_at(0);
        let rest = str.substr(1, str.length());
        return first.to_upper() @ rest;
    },
    
    reverse: func(str) {
        let result = "";
        for (let i = str.length() - 1; i >= 0; i--) {
            result = result @ str.char_at(i);
        }
        return result;
    },
    
    truncate: func(str, maxLen) {
        if (str.length() <= maxLen) {
            return str;
        }
        return str.substr(0, maxLen - 3) @ "...";
    }
};

Constants Module

// constants.nari

global HTTP_STATUS = {
    OK: 200,
    CREATED: 201,
    BAD_REQUEST: 400,
    UNAUTHORIZED: 401,
    NOT_FOUND: 404,
    SERVER_ERROR: 500
};

global COLORS = {
    RED: "#FF0000",
    GREEN: "#00FF00",
    BLUE: "#0000FF"
};

global CONFIG = {
    MAX_RETRIES: 3,
    TIMEOUT_MS: 5000,
    PAGE_SIZE: 20
};

Type Definitions Module

// types.nari

type User {
    id: number;
    username: string;
    email: string;
    role: string
}

type Post {
    id: number;
    title: string;
    content: string;
    authorId: number;
    createdAt: number
}

type Comment {
    id: number;
    postId: number;
    userId: number;
    text: string
}

Module Patterns

Factory Pattern

// factories.nari

global UserFactory = {
    create: func(username, email) {
        return {
            id: 0,  // Would be set by database
            username: username,
            email: email,
            role: "user",
            createdAt: 0
        };
    },
    
    createAdmin: func(username, email) {
        let user = UserFactory.create(username, email);
        user.role = "admin";
        return user;
    }
};

Singleton Pattern

// logger.nari

global Logger = (func() {
    let instance = null;
    
    return {
        getInstance: func() {
            if (instance == null) {
                instance = {
                    logs: [],
                    log: func(message) {
                        instance.logs.push(message);
                        print("[LOG] " @ message);
                    },
                    getLogs: func() {
                        return instance.logs;
                    }
                };
            }
            return instance;
        }
    };
})();

Best Practices

1. One Module Per File

Keep related functionality together:

// Good: user.nari contains all user-related functions
// Avoid: mixing user, post, and comment logic in one file

2. Explicit Exports via Globals

Make it clear what's exported:

// user.nari

// Private helper
func validateEmail(email) {
    return email.index_of("@") != -1;
}

// Public API
global User = {
    create: func(name, email) {
        if (!validateEmail(email)) {
            panic("Invalid email");
        }
        return { name: name, email: email };
    }
};

3. Avoid Global Pollution

Use namespaces to group related functions:

// Good
global MathUtils = {
    add: func(a, b) { return a + b; },
    multiply: func(a, b) { return a * b; }
};

// Avoid
global add = func(a, b) { return a + b; };
global multiply = func(a, b) { return a * b; };

4. Document Dependencies

// user_controller.nari
// Dependencies: user_model.nari, validation.nari

import "user_model.nari";
import "validation.nari";

// ...

Limitations

Current module system limitations:

  1. No aliases: Can't rename imports
  2. No dynamic imports: Must be static at top level

Next Steps