Introducción a los módulos en Lua
A medida que tus programas en Lua crecen, encontrarás que tener todo el código en un solo archivo se vuelve difícil de manejar. Los módulos te permiten organizar tu código en partes separadas y reutilizables. En este artículo aprenderás qué son los módulos, cómo crearlos y cómo usarlos en tus programas.
¿Qué es un módulo?
Un módulo es simplemente un archivo de Lua que contiene funciones y variables que pueden ser utilizadas en otros programas. Piensa en un módulo como una caja de herramientas: contiene las herramientas (funciones) que necesitas para realizar tareas específicas.
Por ejemplo, si estás creando un programa que necesita hacer cálculos matemáticos avanzados, podrías crear un módulo con todas esas funciones matemáticas. Luego, cualquier programa que necesite esas funciones puede simplemente usar ese módulo en lugar de tener que escribir todo el código otra vez.
Ventajas de usar módulos
Usar módulos en tus programas tiene varios beneficios importantes:
Organización: Mantiene tu código ordenado y fácil de encontrar. En lugar de tener un archivo gigante con cientos de líneas, puedes tener varios archivos más pequeños, cada uno con una función específica.
Reutilización: Una vez que creas un módulo, puedes usarlo en muchos programas diferentes. No necesitas copiar y pegar el mismo código una y otra vez.
Mantenimiento: Si necesitas corregir un error o mejorar una función, solo tienes que cambiarla en un lugar (el módulo), y todos los programas que usan ese módulo se beneficiarán automáticamente.
Colaboración: Cuando trabajas en equipo, diferentes personas pueden trabajar en módulos diferentes sin interferir entre sí.
Creando tu primer módulo
Vamos a crear un módulo simple que contenga funciones matemáticas básicas. Primero, crea un nuevo archivo llamado matematicas.lua:
-- matematicas.lua
-- Módulo con operaciones matemáticas básicas
-- Creamos una tabla que contendrá nuestras funciones
local M = {}
-- Función que suma dos números
function M.sumar(a, b)
return a + b
end
-- Función que resta dos números
function M.restar(a, b)
return a - b
end
-- Función que multiplica dos números
function M.multiplicar(a, b)
return a * b
end
-- Función que divide dos números
function M.dividir(a, b)
if b == 0 then
return nil, "Error: no se puede dividir por cero"
end
return a / b
end
-- Devolvemos la tabla con nuestras funciones
return M
Analicemos lo que acabamos de hacer:
-
Creamos una tabla llamada
M(de "Módulo"). Esta tabla contendrá todas las funciones que queremos compartir. -
Definimos cuatro funciones:
sumar,restar,multiplicarydividir. Cada una está dentro de la tablaM, por eso usamosM.sumar,M.restar, etc. -
Al final del archivo, usamos
return Mpara devolver la tabla con todas nuestras funciones.
La palabra clave local antes de M es importante: hace que la tabla solo sea accesible dentro de este archivo, lo cual es una buena práctica. Solo las funciones que asignamos a M serán visibles desde fuera.
Usando un módulo
Ahora que tenemos nuestro módulo, vamos a usarlo. Crea un nuevo archivo llamado main.lua en la misma carpeta donde guardaste matematicas.lua:
-- main.lua
-- Programa que usa nuestro módulo de matemáticas
-- Cargamos el módulo con require
local mat = require("matematicas")
-- Ahora podemos usar las funciones del módulo
print("5 + 3 =", mat.sumar(5, 3))
print("10 - 4 =", mat.restar(10, 4))
print("6 * 7 =", mat.multiplicar(6, 7))
-- La función dividir puede devolver un error
local resultado, error = mat.dividir(15, 3)
if resultado then
print("15 / 3 =", resultado)
else
print(error)
end
-- Intentamos dividir por cero
resultado, error = mat.dividir(10, 0)
if resultado then
print("10 / 0 =", resultado)
else
print(error)
end
Cuando ejecutes este programa, verás:
5 + 3 = 8
10 - 4 = 6
6 * 7 = 42
15 / 3 = 5.0
Error: no se puede dividir por cero
La función require
La función require es la que nos permite cargar módulos en Lua. Cuando escribes:
local mat = require("matematicas")
Lua hace lo siguiente:
- Busca un archivo llamado
matematicas.luaen el directorio actual - Ejecuta ese archivo
- Obtiene lo que el archivo devuelve (en nuestro caso, la tabla
M) - Guarda ese resultado en la variable
mat
Es importante saber que require solo carga cada módulo una vez. Si llamas a require("matematicas") varias veces en tu programa, Lua no volverá a ejecutar el archivo cada vez, sino que reutilizará el resultado de la primera carga. Esto hace que tu programa sea más eficiente.
Nombres de archivos y módulos
El nombre que usas en require debe coincidir con el nombre del archivo del módulo, pero sin la extensión .lua. Por ejemplo:
- Archivo:
matematicas.lua→require("matematicas") - Archivo:
utilidades.lua→require("utilidades") - Archivo:
mi_modulo.lua→require("mi_modulo")
Si tu módulo está en una subcarpeta, incluyes el nombre de la carpeta separado por un punto:
- Archivo:
herramientas/texto.lua→require("herramientas.texto")
Funciones privadas en módulos
A veces queremos tener funciones que solo se usan dentro del módulo y no deben ser accesibles desde fuera. Para esto, definimos funciones locales que no agregamos a la tabla del módulo.
Veamos un ejemplo modificando nuestro módulo de matemáticas:
-- matematicas.lua (versión mejorada)
local M = {}
-- Función privada: solo se puede usar dentro de este módulo
local function esNumeroValido(n)
return type(n) == "number"
end
-- Funciones públicas que validan la entrada
function M.sumar(a, b)
if not esNumeroValido(a) or not esNumeroValido(b) then
return nil, "Error: ambos parámetros deben ser números"
end
return a + b
end
function M.restar(a, b)
if not esNumeroValido(a) or not esNumeroValido(b) then
return nil, "Error: ambos parámetros deben ser números"
end
return a - b
end
-- ... resto de las funciones ...
return M
La función esNumeroValido es local y no está en la tabla M, por lo que no se puede llamar desde fuera del módulo:
local mat = require("matematicas")
-- Esto funciona
print(mat.sumar(5, 3))
-- Esto daría error porque esNumeroValido es privada
-- print(mat.esNumeroValido(5)) -- ¡No funcionaría!
Ejemplo práctico: módulo de utilidades de texto
Vamos a crear un módulo más completo que contenga funciones útiles para trabajar con texto. Crea un archivo llamado texto.lua:
-- texto.lua
-- Módulo con funciones útiles para trabajar con cadenas
local M = {}
-- Convierte la primera letra de una cadena a mayúscula
function M.capitalizar(cadena)
if not cadena or #cadena == 0 then
return ""
end
local primera = string.sub(cadena, 1, 1)
local resto = string.sub(cadena, 2)
return string.upper(primera) .. resto
end
-- Cuenta cuántas palabras hay en una cadena
function M.contarPalabras(cadena)
if not cadena or #cadena == 0 then
return 0
end
local contador = 0
-- Separamos por espacios
for palabra in string.gmatch(cadena, "%S+") do
contador = contador + 1
end
return contador
end
-- Invierte una cadena
function M.invertir(cadena)
return string.reverse(cadena)
end
-- Verifica si una cadena está vacía o solo tiene espacios
function M.estaVacia(cadena)
if not cadena then
return true
end
-- Eliminamos espacios y verificamos si queda algo
local sinEspacios = string.gsub(cadena, "%s+", "")
return #sinEspacios == 0
end
-- Repite una cadena n veces
function M.repetir(cadena, veces)
if not cadena or veces < 1 then
return ""
end
local resultado = ""
for i = 1, veces do
resultado = resultado .. cadena
end
return resultado
end
return M
Ahora podemos usar este módulo en nuestro programa:
-- main.lua
local texto = require("texto")
local frase = "hola mundo"
print("Original:", frase)
print("Capitalizada:", texto.capitalizar(frase))
print("Palabras:", texto.contarPalabras(frase))
print("Invertida:", texto.invertir(frase))
print()
local mensaje = " "
print("¿Está vacía?:", texto.estaVacia(mensaje))
print()
print("Repetir:", texto.repetir("Lua ", 5))
Resultado:
Original: hola mundo
Capitalizada: Hola mundo
Palabras: 2
Invertida: odnum aloh
¿Está vacía?: true
Repetir: Lua Lua Lua Lua Lua
Organizando módulos en carpetas
A medida que tu proyecto crece, querrás organizar tus módulos en carpetas. Aquí te muestro una estructura típica:
mi_proyecto/
├── main.lua
├── modulos/
│ ├── matematicas.lua
│ ├── texto.lua
│ └── utilidades.lua
└── config.lua
Para cargar un módulo que está en una carpeta, usa un punto (.) en lugar de una barra (/):
-- Si matematicas.lua está en la carpeta 'modulos'
local mat = require("modulos.matematicas")
Esta es la forma estándar en Lua de trabajar con módulos en subcarpetas.
Ejercicios prácticos
Ejercicio 1: Módulo de conversiones
Crea un módulo llamado conversiones.lua que incluya funciones para:
- Convertir de kilómetros a millas
- Convertir de millas a kilómetros
- Convertir de Celsius a Fahrenheit
- Convertir de Fahrenheit a Celsius
Luego crea un programa que use este módulo para realizar varias conversiones.
Pista: 1 kilómetro = 0.621371 millas, y la fórmula para convertir Celsius a Fahrenheit es: F = (C * 9/5) + 32
Ejercicio 2: Módulo de validación
Crea un módulo llamado validacion.lua que contenga funciones para validar:
- Si una cadena es un email válido (contiene @ y un punto después del @)
- Si un número está en un rango específico
- Si una cadena tiene una longitud mínima y máxima
- Si una cadena contiene solo letras
Crea un programa que pruebe cada una de estas funciones con diferentes valores.
Ejercicio 3: Calculadora modular
Crea un programa calculadora que use módulos:
- Un módulo
operaciones_basicas.luacon suma, resta, multiplicación y división - Un módulo
operaciones_avanzadas.luacon potencia, raíz cuadrada y porcentaje - Un programa principal que presente un menú al usuario y le permita elegir qué operación realizar
Errores comunes y cómo evitarlos
Error 1: Olvidar el return
Si olvidas poner return M al final de tu módulo, require devolverá true en lugar de tu tabla de funciones:
-- matematicas.lua (INCORRECTO)
local M = {}
function M.sumar(a, b)
return a + b
end
-- ¡Falta el return M!
-- main.lua
local mat = require("matematicas")
print(mat.sumar(5, 3)) -- Error: mat es true, no tiene función sumar
Solución: Siempre termina tu módulo con return M.
Error 2: Archivo no encontrado
Si Lua no encuentra tu módulo, obtendrás un error como:
module 'matematicas' not found
Causas comunes:
- El archivo no está en la misma carpeta que tu programa principal
- El nombre en
requireno coincide con el nombre del archivo - Olvidaste la extensión
.luaen el nombre del archivo (debe sermatematicas.lua, nomatematicas)
Solución: Verifica que el archivo existe, está en el lugar correcto y el nombre coincide exactamente.
Error 3: Crear variables globales accidentalmente
Si olvidas usar local en tu módulo, las variables se vuelven globales:
-- matematicas.lua (INCORRECTO)
M = {} -- ¡Sin local! Ahora M es global
function M.sumar(a, b)
return a + b
end
return M
Esto puede causar problemas si diferentes módulos usan el mismo nombre de variable.
Solución: Siempre usa local para las variables en tus módulos.
Buenas prácticas
Al crear módulos, sigue estas recomendaciones:
-
Usa nombres descriptivos: El nombre del módulo debe indicar qué hace.
matematicas.luaes mejor quemod1.lua. -
Una responsabilidad por módulo: Cada módulo debe hacer una cosa específica. No mezcles funciones de texto con funciones matemáticas en el mismo módulo.
-
Documenta tus funciones: Incluye comentarios que expliquen qué hace cada función, qué parámetros recibe y qué devuelve.
-
Valida los parámetros: Verifica que los valores recibidos son del tipo correcto antes de usarlos.
-
Maneja los errores: Cuando algo puede salir mal (como dividir por cero), devuelve
nily un mensaje de error descriptivo. -
Usa local para todo: Tanto la tabla del módulo como las funciones privadas deben ser locales.
Resumen
En este artículo has aprendido:
- Los módulos son archivos de Lua que contienen funciones y variables reutilizables
- Para crear un módulo, devuelves una tabla con las funciones que quieres compartir
- La función
requirese usa para cargar módulos en tu programa - Las funciones locales son privadas y solo se pueden usar dentro del módulo
- Organizar tu código en módulos hace que sea más fácil de mantener y reutilizar
- Seguir buenas prácticas te ayuda a crear módulos de calidad
Los módulos son una herramienta fundamental en Lua. A medida que practiques más, verás que hacen que tus programas sean mucho más organizados y fáciles de trabajar. En el siguiente artículo, aprenderás sobre el manejo básico de errores en Lua, lo que complementará muy bien lo que has aprendido sobre módulos.