Modules & Imports
Modules in Ferret organize code into reusable units. Understanding how modules work is essential for building larger applications.
What is a Module?
Section titled “What is a Module?”Each .fer file is a module. The module is identified by its import path, which can be:
- Standard library - Built-in modules (
std/io,net/http,db/sql) - Local project files - Files in your project
- External packages - Dependencies from
fer.ret - Neighbor packages - Local packages in your workspace
Import Paths
Section titled “Import Paths”Standard Library Imports
Section titled “Standard Library Imports”import "std/io";import "net/http";import "db/sql";No manifest required for stdlib imports.
Project-Local Imports
Section titled “Project-Local Imports”For files within your project:
Directorymyproject
- fer.ret
- main.fer
Directoryutils
- math.fer
- string.fer
# fer.ret[package]name = "myproject"// In main.ferimport "myproject/utils/math";import "myproject/utils/string";
fn main() { let result := math::Add(5, 3);}External Package Imports
Section titled “External Package Imports”For dependencies declared in fer.ret:
# fer.ret[dependencies]logger = "github.com/user/logger@^v1.0.0"import "github.com/user/logger";
fn main() { logger::Info("Hello!");}Neighbor Package Imports
Section titled “Neighbor Package Imports”For local workspace packages:
Directoryworkspace
Directoryshared
- fer.ret (name = “shared”)
- utils.fer
Directorymyapp
- fer.ret
- main.fer
[dependencies]shared = "../shared"// In myapp/main.ferimport "shared/utils";
fn main() { utils::Helper();}Import Alias Restrictions
Section titled “Import Alias Restrictions”import "std/math";
// ❌ Error: 'math' is already used as an import aliaslet math := 42;
// ❌ Error: 'math' is already used as an import aliasfn math() { }
// ❌ Error: 'math' is already used as an import aliastype math struct { .X: i32; }This restriction prevents naming conflicts and keeps your code clear. If you need to use the name, provide a different import alias:
import "std/math" as m; // Use 'm' as the alias instead
// ✅ Now 'math' is available for other useslet math := 42;Exporting from Modules (Capitalization-Based)
Section titled “Exporting from Modules (Capitalization-Based)”Ferret uses capitalization to control symbol visibility, similar to Go:
- Uppercase names are exported (public) - accessible from other modules
- Lowercase names are private - only accessible within the defining module
// Exported (public) - starts with uppercaseconst PI := 3.14159;fn Add(a: i32, b: i32) -> i32 { return a + b; }type Point struct { .X: i32, .Y: i32 };
// Private - starts with lowercaseconst internal_buffer := 256;fn helper() { /* ... */ }type internal struct { .Data: i32 };Struct Field Visibility
Section titled “Struct Field Visibility”Field names in structs follow the capitalization rule:
type Person struct { .Name: str, // Public field (uppercase) .Age: i32, // Public field (uppercase) .ssn: str // Private field (lowercase)};Access rules:
- Public fields (uppercase) → accessible everywhere
- Private fields (lowercase) → only in methods and struct literals
type Account struct { .Owner: str, // Public .balance: i32 // Private};
fn (a: Account) GetBalance() -> i32 { return a.balance; // ✅ OK - method can access}
fn test() { let acc := { .Owner = "Alice", .balance = 1000 } as Account; let owner := acc.Owner; // ✅ OK - public let bal := acc.balance; // ❌ Error - private let bal2 := acc.GetBalance(); // ✅ OK - use method}Using Imports
Section titled “Using Imports”Module Aliases
Section titled “Module Aliases”Import with an alias to shorten names:
import "std/math" as m;import "github.com/user/logger" as log;
fn main() { let result := m::Sqrt(16.0); log::Info("Result calculated");}Default aliases use the last path segment:
import "std/math";→mathimport "github.com/user/logger";→logger
Accessing Module Members
Section titled “Accessing Module Members”Use :: to access exported members:
import "std/io";import "myproject/utils/math";
fn main() { io::Println("Hello!"); let x := math::PI; let p := math::Point{ .X: 5 };}Module Resolution
Section titled “Module Resolution”Ferret resolves imports in this order:
- Cached modules - Already loaded
- Standard library -
std/,net/,db/ - Local project - Files via package name
- Dependencies - From
fer.ret - Error - Not found
Best Practices
Section titled “Best Practices”- ✅ Clear, descriptive module names
- ✅ Single responsibility per module
- ✅ Export only necessary symbols
- ✅ Use aliases for long paths
- ✅ Declare dependencies in
fer.ret - ❌ Avoid circular dependencies
- ❌ Don’t reuse import alias names
Complete Example
Section titled “Complete Example”Directorymyapp
- fer.ret
- main.fer
Directorymodels
- user.fer
Directoryservices
- auth.fer
# fer.ret[package]name = "myapp"
[dependencies]logger = "github.com/user/logger@^v1.0.0"import "std/io";import "github.com/user/logger";import "myapp/models/user";import "myapp/services/auth";
fn main() { logger::Info("Starting..."); let u := user::New("Alice"); auth::Login(u); io::Println("Welcome!");}type User struct { .Name: string, .Id: i32,}
fn New(name: string) -> User { return User{ .Name: name, .Id: 0 };}
// Private helperfn validate(name: string) -> bool { return name != "";}See Also
Section titled “See Also”- Package Manager - Dependency management
- Usage Guide - Working with packages