wandres.dev
LA BIBLIOTECA ESTÁNDAR · pequeña a propósito

El inventario completo

Las nueve bibliotecas que Lua trae de serie, cuántos nombres expone cada una, cómo contarlos desde el propio intérprete y por qué el hecho de que todo el catálogo quepa holgadamente en una sola lección es, en sí mismo, la tesis de diseño del lenguaje.

⏱ 15 min

Hay una prueba que ningún otro lenguaje contemporáneo supera y que Lua supera sin esfuerzo: recorrer entera su biblioteca estándar, nombre por nombre, en una sola sesión de estudio. No se trata de una simplificación pedagógica ni de un resumen de lo importante; hablamos del catálogo completo, incluidas las funciones que casi nadie usa. Son nueve bibliotecas y menos de doscientos nombres, y esa cifra no es un accidente histórico ni una deuda pendiente: es una decisión sostenida durante tres décadas, defendida explícitamente por los autores y con consecuencias que se notan en cada línea de código Lua que se escribe en el mundo. Esta lección levanta el inventario y empieza a leer lo que significa.

🎯 Al terminar esta lección sabrás
  • Enumerar las nueve bibliotecas estándar y el papel que cumple cada una.
  • Contar los nombres de cada biblioteca desde el propio intérprete, sin consultar el manual.
  • Situar el peso real del conjunto y detectar en qué zonas es sorprendentemente densa.
  • Formular la tesis del tamaño mínimo y anticipar sus costes.

El mapa: nueve bibliotecas y un espacio global

Lo primero que hay que entender es que en Lua no existe un sistema de módulos que oculte la biblioteca estándar detrás de importaciones. Todo lo que trae el lenguaje está ya cargado, visible y accesible desde el entorno global, agrupado en ocho tablas más un conjunto de funciones sueltas que llamamos biblioteca básica. Escribir string.format funciona sin ceremonia previa porque string es una tabla ordinaria que vive en _G desde antes de que tu primera línea se ejecute.

flowchart TD
A[Biblioteca estandar de Lua] --> B[Nucleo del lenguaje]
A --> C[Manipulacion de datos]
A --> D[Mundo exterior]
A --> E[Introspeccion]
B --> B1[base y coroutine y package]
C --> C1[string y utf8 y table y math]
D --> D1[io y os]
E --> E1[debug]

Esa clasificación en cuatro grupos no es oficial, pero explica mejor el diseño que el orden alfabético del manual. El núcleo contiene lo que el lenguaje necesita para hablar de sí mismo: cargar código, gestionar corrutinas, manejar errores. El grupo de datos opera sobre los tipos que el lenguaje ya tiene. El grupo del mundo exterior es la única puerta al sistema operativo, y es deliberadamente estrecha. La introspección vive aparte, marcada como peligrosa por el propio manual.

🧱

base y package

Las funciones sin prefijo: print, type, pairs, pcall, select, setmetatable, tonumber y compañía, más require y la maquinaria de búsqueda de módulos que vive en la tabla package.

🧵

coroutine

Ocho funciones para el único mecanismo de concurrencia que el lenguaje ofrece: corrutinas colaborativas de una sola hebra real.

🔤

string, utf8, table, math

El grueso del catálogo. string es la más densa del conjunto, sobre todo por el motor de patrones y por el empaquetado binario.

🚪

io, os, debug

Ficheros, tiempo, entorno y procesos, más el acceso reflexivo a la máquina virtual. Todo lo que depende del sistema anfitrión cabe aquí.

Hay un detalle del que casi nadie repara y que conviene fijar desde el principio: esas tablas no tienen nada de especial. string es una tabla ordinaria, se le pueden añadir campos, se le puede cambiar la metatabla y se puede sustituir entera por otra. La biblioteca estándar no vive en un espacio protegido del intérprete; vive en el mismo entorno global que tus variables y se manipula con las mismas herramientas.

📝
Un caso especial: la metatabla de las cadenas

El único vínculo que el núcleo mantiene con una biblioteca es el de las cadenas. Todo valor de tipo cadena comparte una metatabla cuyo campo __index apunta a la tabla string, y de ahí sale la notación con dos puntos que permite escribir un método sobre un literal. Si alguien reasigna la tabla string, ese vínculo no se rompe, porque apunta a la tabla original y no al nombre global.

El recuento, entrada por entrada

Las cifras aproximadas para Lua 5.4 son estas: la biblioteca básica expone en torno a veinticinco nombres globales, incluidos _G y _VERSION. coroutine tiene ocho. package aporta la función require más ocho campos en su tabla. string llega a diecisiete funciones. utf8 se queda en seis. table en siete. math reúne unos veinticinco nombres entre funciones y constantes. io expone catorce nombres en su tabla más siete métodos sobre los objetos de fichero. os tiene once. debug ronda los dieciséis.

El total se queda por debajo de doscientos nombres. Para poner esa cifra en contexto: la biblioteca estándar de Python distribuye más de doscientos módulos, y uno solo de ellos puede exponer más funciones que las nueve bibliotecas de Lua juntas.

Lo interesante es que no hace falta creerse ninguna de estas cifras. El propio intérprete puede recitarlas, y ese ejercicio enseña más sobre la naturaleza reflexiva del lenguaje que cualquier tabla del manual:

local libs = { "coroutine", "package", "string", "utf8",
               "table", "math", "io", "os", "debug" }

local total = 0
for _, nombre in ipairs(libs) do
  local n = 0
  for _ in pairs(_G[nombre]) do n = n + 1 end
  print(string.format("%-10s %3d", nombre, n))
  total = total + n
end

-- la biblioteca basica son los globales que no son tablas de biblioteca
local sueltos = 0
for k, v in pairs(_G) do
  if type(v) ~= "table" then sueltos = sueltos + 1 end
end

print(string.format("%-10s %3d", "base", sueltos))
print("total aproximado:", total + sueltos)

Ese fragmento de doce líneas audita la totalidad de lo que el lenguaje te regala. Es difícil exagerar hasta qué punto eso es anómalo. En la mayoría de los entornos modernos, enumerar la biblioteca estándar no es un bucle, sino un proyecto.

El mismo mecanismo sirve para el ejercicio que de verdad enseña: leer los nombres, no contarlos. Ordenarlos alfabéticamente y recorrerlos despacio revela siempre tres o cuatro funciones cuya existencia se ignoraba, y ese hallazgo es el rendimiento inmediato de tener una biblioteca abarcable.

local function nombres_de(t)
  local ns = {}
  for k in pairs(t) do ns[#ns + 1] = tostring(k) end
  table.sort(ns)
  return ns
end

print(table.concat(nombres_de(table), "  "))
--> concat  insert  move  pack  remove  sort  unpack

print(table.concat(nombres_de(os), "  "))
--> clock  date  difftime  execute  exit  getenv  remove  rename
--> setlocale  time  tmpname

Merece la pena detenerse en la segunda salida. Once nombres son todo lo que Lua sabe decir sobre el sistema operativo que lo ejecuta. No hay proceso, ni usuario, ni directorio, ni señal, ni permiso. Un lenguaje que aspira a controlar la máquina no puede tener esa lista; uno que aspira a vivir dentro de cualquier máquina no puede tener otra.

ℹ️
El recuento cambia entre versiones y siempre a la baja

Lua 5.1 tenía loadstring, setfenv, getfenv, module, table.maxn y math.log10. Todas desaparecieron. Lo que entró después —utf8, string.pack, math.tointeger, coroutine.close, warn— llegó en dosis pequeñas y tras años de discusión pública. La biblioteca no crece por acumulación: se poda tanto como se añade.

Una consecuencia poco conocida de que las bibliotecas sean tablas ordinarias es que se pueden retirar. Un anfitrión que empotre Lua elige cuáles abrir, y un programa que ejecute código no confiable puede vaciar las peligrosas antes de cederle el control. El propio manual señala que debug debe usarse con cuidado y que varias de sus funciones pueden romper invariantes del intérprete.

-- Recortar el entorno antes de ejecutar codigo ajeno
os, io, debug, package = nil, nil, nil, nil
collectgarbage()

Ese recorte es una defensa parcial, no un aislamiento real —el nivel de entornos y sustitución de variables globales explica por qué—, pero ilustra hasta qué punto la biblioteca estándar es material ordinario del programa y no una prerrogativa del intérprete.

Dónde está el peso y dónde la ausencia

Contar nombres es engañoso si no se pesa cada uno. La función string.gsub cuenta como una entrada, pero encierra un motor de emparejamiento de patrones completo, con clases de caracteres, capturas, anclas y tres modos distintos de sustitución según el tipo del tercer argumento. string.pack cuenta como una y contiene un minilenguaje de descripción de formatos binarios. os.date cuenta como una y admite una cadena de formato con más de veinte especificadores. La densidad conceptual no está repartida de forma uniforme: unas pocas funciones concentran la mayor parte de lo que hay que aprender.

En el extremo contrario están las bibliotecas que son casi anecdóticas. utf8 tiene seis nombres y ninguno de ellos normaliza, compara ni segmenta texto: se limita a codificar, decodificar y recorrer puntos de código. os tiene once y ni siquiera puede listar un directorio. io no puede saber si un fichero existe sin intentar abrirlo.

Esa asimetría es la primera pista del criterio de diseño. Lua incluye lo que no se puede escribir eficientemente en Lua —el motor de patrones, el empaquetado binario, la aritmética de coma flotante— y omite lo que sí se puede escribir encima, o lo que dependería de detalles del sistema anfitrión que el lenguaje se niega a fijar. El resultado es una biblioteca que no es pequeña por pobreza, sino por criterio de frontera.

Hay una segunda lectura del inventario que solo aparece cuando se compara con el tamaño del núcleo. El intérprete completo, con las nueve bibliotecas dentro, produce un binario del orden de unos pocos cientos de kilobytes. La biblioteca estándar no es un anexo voluminoso sobre un lenguaje pequeño: es una fracción modesta de algo que ya era pequeño. Esa proporción es la que permite que Lua viva dentro de un microcontrolador, de un enrutador, de un motor de videojuego o de un editor de texto sin que nadie tenga que discutir el presupuesto de memoria.

💡
Aprende la biblioteca leyendo el manual entero una vez

El manual de referencia de Lua cabe en unas cien páginas y la sección de bibliotecas ocupa apenas un tercio de ellas. Leerlo de principio a fin es una tarde de trabajo y es, con diferencia, la inversión de mayor rendimiento que puede hacer alguien que vaya a escribir Lua durante años. Con ningún otro lenguaje de uso general esa recomendación sería realista.

El tamaño es la característica, no la limitación

Conviene detenerse en lo que significa que una biblioteca estándar completa quepa en una lección. Significa que un programador puede alcanzar, en semanas y no en años, un estado que en otros ecosistemas resulta inalcanzable: saber con certeza todo lo que el lenguaje ofrece. Ese estado tiene consecuencias enormes y poco discutidas. La primera es que desaparece una clase entera de errores: nadie reimplementa una función que ya existe, porque nadie ignora que existe. La segunda es que desaparece la parálisis de elección; cuando hay siete funciones para manipular secuencias y no setenta, la pregunta de cuál usar deja de consumir atención. La tercera, y la más profunda, es que el conocimiento no caduca: quien aprendió esta biblioteca en 2006 la sigue conociendo entera hoy, porque los cambios han sido pocos, deliberados y documentados uno por uno. Frente a esto, la crítica habitual —que Lua no trae nada— confunde el catálogo con el poder expresivo. El lenguaje no trae un cliente HTTP porque el lenguaje no sabe qué es una red, y esa ignorancia es precisamente lo que le permite ejecutarse dentro de un microcontrolador, de un motor de videojuego, de un servidor web y de tu editor de texto sin cambiar una línea de su código fuente. La biblioteca estándar de Lua es pequeña porque es portable, y es portable porque se negó a suponer nada sobre la máquina que la ejecuta. Todo lo que sigue en este nivel es el desarrollo de esa única idea.

⚔️ Audita el lenguaje entero
  1. Ejecuta el fragmento de recuento en tu intérprete y anota las cifras exactas de tu versión.
  2. Repite el recuento en una versión distinta de Lua o en LuaJIT y elabora la lista de diferencias.
  3. Escribe un bucle que imprima, para cada biblioteca, sus nombres ordenados alfabéticamente; recorre esa lista y marca los que nunca has usado.
  4. Localiza tres nombres del inventario cuya existencia desconocías y averigua en el manual qué problema resuelve cada uno.
  5. Estima cuántas líneas de Lua harían falta para reimplementar table.concat y cuántas para reimplementar string.gsub; justifica la diferencia.