N

Nanyx

Documentation

Modules

Modules in Nanyx help organize code into logical units. They provide namespaces for definitions and control visibility.

Module Declaration

Every Nanyx file starts with a module declaration:

module MyApp

def main = {
  println("Hello from MyApp!")
}

Module Names

Module names should be PascalCase:

module UserManagement
module DataProcessing  
module HttpClient

Importing Modules

Import other modules to use their exports:

module MyApp

import UserManagement
import DataProcessing

def main = {
  UserManagement.createUser(...)
  DataProcessing.process(...)
}

Qualified Imports

Access module members with the module name:

import Math

def result = Math.sqrt(16)  -- 4.0
def pi = Math.pi

Selective Imports

Import specific items from a module:

import Math (sqrt, pi, cos)

def result = sqrt(16)  -- No need for Math. prefix

Import Aliases

Give modules shorter names:

import DataProcessing as DP

def result = DP.process(data)

Exports

By default, all top-level definitions are exported:

module Utils

-- Exported (public)
def double: int -> int = { x -> x * 2 }

-- Also exported
def triple: int -> int = { x -> x * 3 }

Private Definitions

Use private to hide internal implementation details:

module Utils

-- Public
def processData: Data -> Result = { data ->
  data \validate \transform
}

-- Private helper
private def validate: Data -> Data = { data ->
  -- validation logic
}

private def transform: Data -> Data = { data ->
  -- transformation logic
}

Module Structure

Organize related functionality:

module Collections.List

def map: (list(a), (a -> b)) -> list(b) = { ... }
def filter: (list(a), (a -> bool)) -> list(a) = { ... }
def fold: (list(a), b, (b, a) -> b) -> b = { ... }
module Collections.Map

def empty: map(k, v) = { ... }
def insert: (map(k, v), k, v) -> map(k, v) = { ... }
def lookup: (map(k, v), k) -> Option(v) = { ... }

Nested Modules

Create hierarchies with dot notation:

module MyApp.Users.Validation

def validateEmail: string -> Result(Email, ValidationError) = { ... }
def validateAge: int -> Result(Age, ValidationError) = { ... }

Re-exporting

Export items from other modules:

module Collections

-- Re-export from sub-modules
export Collections.List (map, filter, fold)
export Collections.Map (empty, insert, lookup)

Module-Level Constants

Define constants at module level:

module Config

def AppName = "MyApp"
def Version = "1.0.0"
def MaxRetries = 3

def DatabaseConfig = (
  host = "localhost"
  port = 5432
  database = "myapp"
)

Circular Dependencies

Avoid circular module dependencies. If module A imports B, then B cannot import A.

Instead, extract shared code to a third module:

-- Bad: circular dependency
module A
import B  -- A imports B

module B  
import A  -- B imports A (circular!)

-- Good: extract shared code
module Shared
-- Common definitions

module A
import Shared

module B
import Shared

Standard Library Modules

Nanyx's standard library is organized into modules:

import List
import Map
import Set
import String
import Math
import Option
import Result

Example: User Management Module

module UserManagement

type User = (
  id: UserId
  name: string
  email: Email
  role: #admin | #user
)

type ValidationError =
  | #invalidEmail
  | #nameTooShort
  | #emailTaken

def createUser: (string, Email) -> Result(User, ValidationError) = { name, email ->
  if name.length < 3
    -> #error(#nameTooShort)
  else
    def id = generateId()
    #ok((id = id, name = name, email = email, role = #user))
}

def validateEmail: Email -> Result(Email, ValidationError) = { email ->
  if email \contains("@")
    -> #ok(email)
    else -> #error(#invalidEmail)
}

private def generateId: () -> UserId = {
  -- Implementation detail, not exported
  UserId(randomInt())
}

Module Organization Best Practices

  1. One module per file: Keep modules focused and manageable
  2. Clear naming: Use descriptive names that reflect the module's purpose
  3. Hide implementation: Mark helpers as private
  4. Cohesion: Group related functionality together
  5. Minimal coupling: Reduce dependencies between modules
  6. No circular deps: Keep dependency graph acyclic

Using Modules

module MyApp

import UserManagement as Users
import DataProcessing

def main = {
  def result = Users.createUser("Alice", "alice@example.com")
  
  match result
    | #ok(user) ->
        user \DataProcessing.process \save
    | #error(err) ->
        err \logError
}