Nota: este post aplica a versiones de LUA 5.1 o inferiores.
Anuncios

Bienvenidos sean a este post, un gran inconveniente de ese metodo basico para crear modulos es que requiere una especial atencion del programador, se debe calificar el nombre cuando se acceden a otras entidades publicas dentro del mismo modulo, se tiene que cambiar cuando cambia el estado de una funcion de privado a publico (o viceversa) mas aun es demasiado facil olvidarse de la palabra local en una declaracion privada.

Los entornos de funcion ofrecen una tecnica interesante para resolver estos problemas a la hora de la creacion de los modulos, una vez que el chunk principal del modulo tiene un entorno exclusivo no solo todas las funciones comparten esta tabla, por lo tanto podemos declarar todas las funciones publicas como variables globales e iran a separar la tabla automaticamente, todo lo que el modulo tiene que asignar esta tabla al nombre del modulo y tambien a package.loaded, para ilustrar esto veamos el siguiente codigo:

local nombremod = ...
local M = {}
_G[nombremod] = M
package.loaded[nombremod] = M
setfenv(1, M)
Anuncios

Tomemos el ejemplo de este post, ahora cuando declaramos a la funcion agregar va a complejo.agregar:

function agregar(c1, c2)
	return nuevo(c1.r + c2.r, c1.i + c2.i)
end

A partir de ahora podemos llamar a otras funciones del mismo modulo sin ningun prefijo, por ejemplo agregar consigue a nuevo de su entorno que en realidad es complejo.nuevo, este metodo ofrece un buen soporte para modulos con poco trabajo extra para el programador, sin necesidad de prefijar todo porque no hay diferencia entre llamar a un exportado y una funcion privada ya que si el programador olvida un local este no modifica el entorno global sino que una funcion privada se convierte en una funcion publica.

Anuncios

Hasta aqui una cosa que nos falta es el acceso a otros modulos porque una vez que creamos la tabla vacia M como nuestro entorno perdemos acceso a todas las variables globales previas pero hay varias formas de recuperar este acceso y cada cual tiene sus pros y contras, una de las soluciones mas simples es por herencia, como vimos anteriormente:

local nombremod = ...
local M = {}
_G[nombremod] = M
package.loaded[nombremod] = M
setmetatable(M, {__index = _G})
setfenv(1, M)

Con esta construccion el modulo tiene acceso directo a cualquier identificador global con una pequeña sobrecarga para cada acceso, una consecuencia interesante de esta solucion es que, conceptualmente, tu modulo contiene todas las variables globales, por ejemplo alguien que usa tu modulo podria llamar a la funcion estandar de seno escribiendo complejo.math.sin(x), es una forma similar al sistema de paquetes de Perl, otro metodo rapido de acceder otros modulos es declarar un local que contenga el entorno viejo:

local nombremod = ...
local M = {}
_G[nombremod] = M
package.loaded[nombremod] = M
setmetatable(M, {__index = _G})
local _G = _G
setfenv(1, M)
Anuncios

Lo unico que a partir de ahora debes prefijar a cualquier variable global con _G pero el acceso sera un poco mas rapido porque no hay ningun metametodo envuelto, un enfoque mas disciplinado es declarar como locales solo las funciones que necesitas o la mayoria de los modulos que necesites:

local nombremod = ...
local M = {}
_G[nombremod] = M
package.loaded[nombremod] = M

-- Importar seccion:
-- declara todo lo que este modulo necesita desde afuera
local sqrt = math.sqrt
local io = io

-- no mas accesos externos despues de este punti
setfenv(1, M)

Si bien esta tecnica demanda mas trabajo, nos permite una mejor documentacion de las dependencias del modulo pero tambien resulta en un codigo que corre mas rapido que cualquiera de los codigos con los esquemas anteriores.

Anuncios

En resumen, hoy hemos visto como usar entornos para mejorar lo visto en este post, hemos visto como mejorarlo paso a paso hasta lograr una mejor solucion, no mas facil pero si mejor documentada para una futura revision, espero les haya sido util sigueme en Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Tengo un Patreon donde podes acceder de manera exclusiva a material para este blog antes de ser publicado, sigue los pasos del link para saber como.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00