Skip to content

Latest commit

 

History

History
468 lines (350 loc) · 7.56 KB

File metadata and controls

468 lines (350 loc) · 7.56 KB

Module System

AffineScript's module system organizes code into logical units with controlled visibility.

Table of Contents

  1. Module Basics
  2. File Structure
  3. Visibility
  4. Imports
  5. Re-exports
  6. Namespacing
  7. Module Patterns

Module Basics

Declaring Modules

// In main.affine
mod math        // Looks for math.affine or math/mod.affine
mod utils       // Looks for utils.affine or utils/mod.affine
mod internal    // Inline module definition follows

mod internal {
  fn helper() -> Int { 42 }
}

Module Contents

Modules can contain:

  • Functions
  • Types (structs, enums, type aliases)
  • Traits
  • Effects
  • Constants
  • Nested modules
mod geometry {
  struct Point { x: Float64, y: Float64 }
  struct Circle { center: Point, radius: Float64 }

  trait Shape {
    fn area(self: &Self) -> Float64
  }

  impl Shape for Circle {
    fn area(self: &Self) -> Float64 {
      3.14159 * self.radius * self.radius
    }
  }

  fn distance(p1: &Point, p2: &Point) -> Float64 {
    let dx = p2.x - p1.x
    let dy = p2.y - p1.y
    (dx * dx + dy * dy).sqrt()
  }
}

File Structure

Single File Modules

project/
├── main.affine
├── math.affine       # mod math
└── utils.affine      # mod utils

Directory Modules

project/
├── main.affine
└── math/
    ├── mod.affine      # mod math entry point
    ├── vectors.affine  # mod vectors (inside math)
    └── matrices.affine # mod matrices (inside math)

math/mod.affine:

// Declare submodules
pub mod vectors
pub mod matrices

// Re-export commonly used items
pub use vectors::Vec3
pub use matrices::Mat4

Nested Modules

project/
├── main.affine
└── graphics/
    ├── mod.affine
    ├── renderer/
    │   ├── mod.affine
    │   ├── opengl.affine
    │   └── vulkan.affine
    └── primitives/
        ├── mod.affine
        ├── shapes.affine
        └── colors.affine

Visibility

Default Visibility

Items are private by default:

mod internal {
  fn private_helper() -> Int { 42 }  // Private to this module
}

// main.affine
internal::private_helper()  // ERROR: private function

Public Items

Use pub to make items public:

mod math {
  pub fn add(x: Int, y: Int) -> Int { x + y }

  fn internal_helper() -> Int { 0 }  // Still private
}

// main.affine
math::add(1, 2)  // OK

Visibility Modifiers

pub           // Public to everyone
pub(crate)    // Public within the crate
pub(super)    // Public to parent module
pub(in path)  // Public to specific module path
              // (no modifier) - Private to current module

Examples:

mod outer {
  pub mod inner {
    pub fn public_fn() { }           // Everyone can access
    pub(super) fn parent_only() { }  // Only outer can access
    pub(crate) fn crate_only() { }   // Only this crate
    fn private_fn() { }               // Only inner can access
  }

  fn test() {
    inner::public_fn()    // OK
    inner::parent_only()  // OK - we're the parent
    inner::private_fn()   // ERROR - private
  }
}

Struct Field Visibility

pub struct Config {
  pub name: String,          // Public field
  pub(crate) internal: Int,  // Crate-visible field
  secret: String,            // Private field
}

impl Config {
  // Constructor needed for private fields
  pub fn new(name: String, secret: String) -> Config {
    Config { name, internal: 0, secret }
  }
}

Imports

Basic Imports

use math::add
use math::sub

fn main() {
  add(1, 2)  // No prefix needed
}

Grouped Imports

use math::{add, sub, mul, div}

use std::collections::{Vec, HashMap, HashSet}

Nested Imports

use std::{
  io::{Read, Write, BufReader},
  collections::{Vec, HashMap},
  fmt::Show
}

Glob Imports

use math::*  // Import all public items

// Use sparingly - can cause name conflicts

Aliased Imports

use std::collections::HashMap as Map
use graphics::renderer::opengl as gl

let map: Map[String, Int] = Map::new()
gl::init()

Self Import

use std::collections::{self, HashMap}

// Now can use both:
collections::Vec  // Full path
HashMap           // Direct

Re-exports

Basic Re-export

// In lib.affine
mod internal

pub use internal::PublicType
pub use internal::public_function

Aliased Re-export

pub use internal::LongTypeName as Short

Glob Re-export

// Re-export everything from a module
pub use prelude::*

Creating a Prelude

// prelude.affine
pub use crate::types::{Result, Option, Error}
pub use crate::traits::{Show, Clone, Default}
pub use crate::macros::*

// Users can import everything commonly needed:
use mylib::prelude::*

Namespacing

Path Resolution

// Absolute path from crate root
use crate::module::item

// Relative to current module
use self::submodule::item

// Relative to parent module
use super::sibling::item

// External crate
use external_crate::item

Name Shadowing

use std::option::Option

// Local definition shadows import
enum Option[T] {
  Some(T),
  None,
  Unknown
}

// Use explicit path for std version
let x: std::option::Option[Int] = std::option::Some(42)
let y: Option[Int] = Unknown

Disambiguating

use graphics::Point
use geometry::Point as GeoPoint

let screen_pt: Point = Point { x: 100, y: 200 }
let world_pt: GeoPoint = GeoPoint { x: 1.0, y: 2.0 }

Module Patterns

Facade Pattern

// lib.affine - expose clean public API
pub mod types
pub mod traits
pub mod error

// Re-export main items at crate root
pub use types::{Config, Settings}
pub use traits::Processor
pub use error::{Error, Result}

// Keep implementation private
mod internal {
  // Implementation details
}

Feature Modules

// Conditionally compiled modules
#[cfg(feature = "async")]
pub mod async_support

#[cfg(feature = "serde")]
pub mod serialization

Test Modules

pub fn add(x: Int, y: Int) -> Int {
  x + y
}

#[cfg(test)]
mod tests {
  use super::*

  #[test]
  fn test_add() {
    assert_eq(add(2, 2), 4)
  }
}

Internal vs External API

// Public API
pub mod api {
  pub use crate::core::PublicType
  pub use crate::core::public_fn
}

// Internal implementation
mod core {
  pub struct PublicType { ... }
  pub fn public_fn() { ... }

  // These stay internal
  struct InternalHelper { ... }
  fn internal_fn() { ... }
}

Crate Structure

Library Crate

mylib/
├── affine.toml
└── src/
    ├── lib.affine      # Crate root
    ├── types.affine
    ├── traits.affine
    └── utils/
        ├── mod.affine
        └── helpers.affine

lib.affine:

pub mod types
pub mod traits
pub mod utils

pub use types::MainType
pub use traits::MainTrait

Binary Crate

myapp/
├── affine.toml
└── src/
    ├── main.affine     # Binary entry point
    └── lib.affine      # Optional library part

Mixed Crate

myproject/
├── affine.toml
└── src/
    ├── main.affine     # Uses lib
    ├── lib.affine      # Library code
    └── modules/

See Also