Chuyển tới nội dung chính

Modules trong Aiken

Bài học này hướng dẫn cách tổ chức code thành modules và quản lý visibility.

Mục tiêu học tập

  • Hiểu cách tổ chức code thành modules
  • Nắm các kiểu import: qualified và unqualified
  • Sử dụng pub để quản lý visibility
  • Hiểu opaque types

Module Basics

Tên module từ đường dẫn file

lib/
├── utils.ak → module: utils
├── helpers/
│ ├── math.ak → module: helpers/math
│ └── string.ak → module: helpers/string
└── validators/
└── spend.ak → module: validators/spend

Định nghĩa module

lib/math.ak
//// Module xử lý toán học
//// Sử dụng //// cho module-level docs

/// Cộng hai số
pub fn add(a: Int, b: Int) -> Int {
a + b
}

/// Nhân hai số
pub fn multiply(a: Int, b: Int) -> Int {
a * b
}

// Hàm private - không thể import từ bên ngoài
fn internal_helper(x: Int) -> Int {
x * 2
}

Visibility - Phạm vi truy cập

Public vs Private

lib/wallet.ak
// ❌ Private - mặc định
fn calculate_fee(amount: Int) -> Int {
amount / 100
}

// ✅ Public - có thể import
pub fn transfer(from: ByteArray, to: ByteArray, amount: Int) -> Bool {
let fee = calculate_fee(amount)
// Logic transfer
True
}

// ❌ Private type
type InternalState {
value: Int,
}

// ✅ Public type
pub type Transaction {
sender: ByteArray,
receiver: ByteArray,
amount: Int,
}

Public constant

// ❌ Private constant
const internal_fee = 1000

// ✅ Public constant
pub const min_utxo_value = 1_000_000
pub const ada_to_lovelace = 1_000_000

Import Styles

Qualified Import

lib/main.ak
use helpers/math

fn calculate() -> Int {
// Sử dụng với prefix module
math.add(10, 20)
}

Alias Import

use helpers/math as m

fn calculate() -> Int {
m.add(10, 20)
}

Unqualified Import

use helpers/math.{add, multiply}

fn calculate() -> Int {
// Sử dụng trực tiếp không cần prefix
add(10, multiply(5, 4))
}

Mix Import Styles

use helpers/math.{add} as m

fn calculate() -> Int {
// Cả hai cách đều hoạt động
add(10, 20) // Unqualified
m.multiply(5, 4) // Qualified với alias
}

Opaque Types

Ẩn implementation, chỉ expose API:

lib/amount.ak
/// Lovelace amount - không thể tạo trực tiếp từ bên ngoài
pub opaque type Lovelace {
Lovelace(Int)
}

/// Constructor - cách duy nhất để tạo Lovelace
pub fn from_int(n: Int) -> Option<Lovelace> {
if n >= 0 {
Some(Lovelace(n))
} else {
None
}
}

/// Accessor - cách duy nhất để lấy giá trị
pub fn to_int(amount: Lovelace) -> Int {
let Lovelace(n) = amount
n
}

/// Safe addition
pub fn add(a: Lovelace, b: Lovelace) -> Lovelace {
Lovelace(to_int(a) + to_int(b))
}

Sử dụng từ module khác:

lib/payment.ak
use amount.{Lovelace}

fn process_payment() {
// ✅ Tạo qua constructor function
expect Some(amt) = amount.from_int(1_000_000)

// ❌ Không thể tạo trực tiếp
// let amt = Lovelace(1_000_000) // Lỗi!

// ❌ Không thể pattern match
// let Lovelace(n) = amt // Lỗi!

// ✅ Dùng accessor
let value = amount.to_int(amt)

value
}

Prelude - Module tự động import

Các items tự động có sẵn không cần import:

// Tự động có sẵn từ prelude
let some_value: Option<Int> = Some(42)
let no_value: Option<Int> = None
let nothing: Void = Void
let ordering: Ordering = Less

Builtin Module

Truy cập các hàm Plutus Core:

use aiken/builtin

fn hash_data(data: ByteArray) -> ByteArray {
builtin.blake2b_256(data)
}

fn verify_ed25519(key: ByteArray, msg: ByteArray, sig: ByteArray) -> Bool {
builtin.verify_ed25519_signature(key, msg, sig)
}

Sơ đồ module system

┌─────────────────────────────────────────────────────────────┐
│ MODULE SYSTEM │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ File Path │ ──▶ │ Module Name │ │
│ └─────────────┘ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Visibility Control │ │
│ │ ┌─────────┐ ┌─────────┐ │ │
│ │ │ pub │ │ private │ │ │
│ │ │ (export)│ │(default)│ │ │
│ │ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Import Styles │ │
│ │ • use module │ │
│ │ • use module as alias │ │
│ │ • use module.{item1, item2} │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

Bước tiếp theo

Trong bài tiếp theo, chúng ta sẽ học về Data - cách serialize và deserialize dữ liệu trong Aiken.