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 HttpClientImporting 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.piSelective Imports
Import specific items from a module:
import Math (sqrt, pi, cos)
def result = sqrt(16) -- No need for Math. prefixImport 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 SharedStandard Library Modules
Nanyx's standard library is organized into modules:
import List
import Map
import Set
import String
import Math
import Option
import ResultExample: 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
- One module per file: Keep modules focused and manageable
- Clear naming: Use descriptive names that reflect the module's purpose
- Hide implementation: Mark helpers as private
- Cohesion: Group related functionality together
- Minimal coupling: Reduce dependencies between modules
- 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
}