Índice

Imports y módulos

¿Por qué módulos?

En la Parte 1, cada programa era un solo archivo. Eso funciona bien para programas pequeños, pero imagina un programa con 50 funciones — encontrar cualquier cosa se vuelve una pesadilla. Los módulos te permiten dividir el código en archivos separados, cada uno enfocado en un tema.

Piensa en los módulos como capítulos de un libro. Cada capítulo cubre un tema, y puedes ir al capítulo que necesitas. En Nyx, cada archivo .nx puede ser un módulo.

Tu primer módulo

Creemos un módulo de ayudantes matemáticos. Crea un archivo llamado math_helpers.nx:

// math_helpers.nx

export fn cuadrado(x: int) -> int {
    return x * x
}

export fn cubo(x: int) -> int {
    return x * x * x
}

export fn factorial(n: int) -> int {
    if n <= 1 { return 1 }
    return n * factorial(n - 1)
}

La palabra clave export hace que una función esté disponible para otros archivos. Sin export, la función es privada — solo se puede usar dentro de su propio archivo.

Ahora crea tu programa principal que lo usa:

// main.nx

import { cuadrado, cubo, factorial } from "math_helpers"

fn main() {
    print(cuadrado(5))     // 25
    print(cubo(3))         // 27
    print(factorial(6))    // 720
}

La declaración import trae funciones específicas de otro módulo a tu archivo.

La sintaxis de import

import { nombre1, nombre2, nombre3 } from "nombre_modulo"

Exportar structs

También puedes exportar structs:

// formas.nx

export struct Circulo {
    radio: int
}

export struct Rectangulo {
    ancho: int,
    alto: int
}

export fn area_circulo(c: Circulo) -> int {
    return 3 * c.radio * c.radio
}

export fn area_rectangulo(r: Rectangulo) -> int {
    return r.ancho * r.alto
}
// main.nx

import { Circulo, Rectangulo, area_circulo, area_rectangulo } from "formas"

fn main() {
    let c: Circulo = Circulo { radio: 10 }
    let r: Rectangulo = Rectangulo { ancho: 5, alto: 8 }

    print(area_circulo(c))       // 300
    print(area_rectangulo(r))    // 40
}

Dónde busca Nyx los módulos

Cuando escribes import "math_helpers", Nyx busca en este orden:

  1. Directorio del proyectomath_helpers.nx relativo a la raiz del proyecto
  2. Paquetes instaladospackages/pkg-name/rest.nx para dependencias agregadas via nyx add
  3. Directorio localmath_helpers.nx junto a tu archivo
  4. Biblioteca estándarstd/math_helpers.nx en la instalacion de Nyx

Esto significa que puedes importar de la biblioteca estandar, tus propios archivos y paquetes instalados de la misma manera.

Usar la biblioteca estándar

Nyx viene con una biblioteca estándar de módulos útiles. Algunos ejemplos:

import { read_file, write_file } from "std/file"
import { http_serve, http_response } from "std/http"
import { json_parse, json_stringify } from "std/json"

El prefijo std/ le dice a Nyx que busque en el directorio de la biblioteca estándar.

Organizar un proyecto

Un proyecto típico de Nyx podría verse así:

mi_proyecto/
├── main.nx           # Punto de entrada
├── modelos.nx        # Estructuras de datos
├── base_datos.nx     # Operaciones de base de datos
└── utilidades.nx     # Funciones auxiliares
// modelos.nx
export struct Usuario {
    nombre: String,
    email: String,
    edad: int
}

export struct Publicacion {
    titulo: String,
    cuerpo: String,
    autor: String
}
// utilidades.nx
export fn repetir_string(s: String, veces: int) -> String {
    var resultado: String = ""
    var i: int = 0
    while i < veces {
        resultado = resultado + s
        i += 1
    }
    return resultado
}

export fn limitar(valor: int, minimo: int, maximo: int) -> int {
    if valor < minimo { return minimo }
    if valor > maximo { return maximo }
    return valor
}
// main.nx
import { Usuario, Publicacion } from "modelos"
import { repetir_string, limitar } from "utilidades"

fn main() {
    let u: Usuario = Usuario { nombre: "Alice", email: "alice@example.com", edad: 30 }
    print(u.nombre)

    print(repetir_string("=-", 20))    // =-=-=-=-=-=-...
    print(limitar(150, 0, 100))        // 100
}

Qué significa export

Solo los elementos con export son visibles fuera del módulo. Esto es importante para ocultar detalles de implementación:

// password.nx

// Privado — solo se puede usar dentro de este archivo
fn hash_interno(s: String, rondas: int) -> String {
    var resultado: String = s
    var i: int = 0
    while i < rondas {
        resultado = resultado + "*"
        i += 1
    }
    return resultado
}

// Público — otros archivos pueden usar esto
export fn hash_password(password: String) -> String {
    return hash_interno(password, 10)
}

Si otro archivo intenta importar hash_interno, el compilador dará un error. Esto protege tu lógica interna.

Ejemplo práctico: una mini biblioteca

Construyamos una pequeña biblioteca de utilidades de strings:

// string_utils.nx

export fn esta_vacio(s: String) -> bool {
    return s.length() == 0
}

export fn empieza_con_mayuscula(s: String) -> bool {
    if s.length() == 0 { return false }
    let c: int = s.charAt(0)
    return c >= 65 and c <= 90
}

export fn contar_palabras(s: String) -> int {
    if s.length() == 0 { return 0 }
    let partes: Array = s.split(" ")
    return partes.length()
}

export fn truncar(s: String, max_len: int) -> String {
    if s.length() <= max_len { return s }
    return s.substring(0, max_len) + "..."
}
// main.nx

import { esta_vacio, empieza_con_mayuscula, contar_palabras, truncar } from "string_utils"

fn main() {
    print(esta_vacio(""))                      // true
    print(esta_vacio("hola"))                  // false
    print(empieza_con_mayuscula("Hola"))       // true
    print(empieza_con_mayuscula("hola"))       // false
    print(contar_palabras("Nyx es un lenguaje compilado"))    // 5
    print(truncar("Hola, Mundo!", 5))          // Hola,...
}

Ejercicios

  1. Crea un módulo math_utils.nx que exporte funciones abs(n: int) -> int, max(a: int, b: int) -> int, y min(a: int, b: int) -> int. Escribe un programa principal que las importe y use.
  1. Crea un módulo validadores.nx que exporte es_positivo(n: int) -> bool, es_par(n: int) -> bool, y en_rango(n: int, minimo: int, maximo: int) -> bool. Úsalos en un programa principal.
  1. Divide el seguimiento de notas del Capítulo 10 en módulos: estudiante.nx (struct + creación), notas.nx (promedio, más alta, letra), y main.nx (informe y punto de entrada).
  1. Crea un módulo array_utils.nx que exporte suma(arr: Array) -> int, promedio(arr: Array) -> int, y contiene(arr: Array, objetivo: int) -> bool. Pruébalo desde un archivo principal.
  1. Crea dos módulos que ambos exporten una función con la misma lógica pero nombres diferentes. Importa ambos en un archivo principal y verifica que funcionen juntos sin conflictos.

Resumen

Siguiente capítulo: Archivos →

← Anterior: Tu primer proyecto Siguiente: Archivos →