#934 closed doubt (remind)
Hierarchical modularity
Reported by: | Pedro Gea | Owned by: | Víctor de Buen Remiro |
---|---|---|---|
Priority: | highest | Milestone: | OOP Implementation |
Component: | OOP | Version: | |
Severity: | blocker | Keywords: | |
Cc: |
Description
Al intentar crear paquetes y usar una programación modular me encuentro ante una situación que no funciona y no estoy seguro si debería funcionar o no.
La situación surge cuando un submódulo (N2) intenta usar alguna función (F1) definida en su mismo ámbito:
NameBlock N0A = [[ Real F1(Real void) { 1 }; NameBlock N2 = [[ Real F2(Real void) { F1(?) } ]] ]]; // F2 no es capaz de localizar a F1 Real N0A::N2::F2(?);
Entiendo que este comportamiento puede ser un poco comprometido, pues el comportamiento de los NameBlock's (como espacios de nombres) es el mismo que el de las instancias y de permitirlo podría ocurrir que una instancia de una clase pudiera acceder a miembros de otra instancia que la contiene.
Lo único que me ocurre es crear todo a nivel de submódulos, pero eso no es cómodo en las circunstancias que me encuentro, pero funciona:
NameBlock N0B = [[ NameBlock N1 = [[ Real F1(Real void) { 1 } ]]; NameBlock N2 = [[ NameBlock N1; Real F2(Real void) { N1::F1(?) } ]] ]]; // F2 ya es capaz de localizar a F1 Real N0B::N2::F2(?);
¿Hay alguna forma natural de comunicarle al módulo N2 el acceso a todos los elementos de N0?
También se me ocurre crear el NameBlock como un singletón (ya que la clase se define en N0 y tiene acceso a sus elementos) aunque parece un poco retorcido:
NameBlock N0C = [[ Real F1(Real void) { 1 }; Class @N2 { Real F2(Real void) { F1(?) } }; @N2 N2 ]]; // F2 es capaz de localizar a F0 Real N0C::N2::F2(?);
¿No podría declararse de alguna manera que N2 tenga la visibilidad del espacio donde se define?
Quizá usando una palabra clave que se anteponga a la declaración como ocurre con "Static":
NameBlock N0A = [[ Real F1(Real void) { 1 }; Friend NameBlock N2 = [[ Real F2(Real void) { F1(?) } ]] ]];
Change History (5)
comment:1 Changed 15 years ago by
Milestone: | → OOP Implementation |
---|
comment:2 Changed 15 years ago by
Quizá el único problema de esto es que hay que ir buscando las funciones que están en estas circunstancias y renombrarlas.
Por eso proponía lo de crear una palabra especial para indicarlo.
La alternativa de crearlo como instancia única funciona y realmente es cómoda pues básicamente es cambiar
NameBlock <nombre> = [[ ... ]];
por
Class @<nombre> { ... }; @<nombre> <nombre>;
y funciona.
Quizá merece la pena considerar esa facilidad que tienen las clases (y de camino sus instancias) para determinados NameBlock's.
De todos modos no es un problema grave, y de un modo u otro saldré del paso.
comment:3 Changed 15 years ago by
Lo de la palabra especial tiene exactamente el mismo problema que dar visibilidad internamente a todos los NameBlock antecesores.
Eso que dices que vas a hacer no me parece ni bien ni mal, funciona así que es correcto. Lo que no veo es que sea menos trabajo que llamar directamente al <nameblock>:: que le corresponde, más bien resulta mucho más trabajo a mi modo de ver. Lo mismo que haces esa sustitución añades a las llamadas a los métodos su prefijo correspondiente que es mucho menos cambio. Supongo que por algún motivo que desconozco tendrás más dificultad para localizar las llamadas que las definiciones.
En cualquier caso esto es una sólo una muestra de un problema general de TOL y de otros muchos lenguajes interpretados: el que un código no dé errores sintácticos no implica que no haya errores semánticos ni de ámbito. En los lenguajes compilados no ocurre así y sólo queda lugar a los errores de lógica o diseño, accesos inválidos y demás.
Quizás convendría pensar un poco acerca de algún modo de chequear ese tipo de cosas mediante algún sistema de diagnosis que genere tests de forma automática para un NameBlock dado. Podría recorrerse los métodos llamándolos con argumentos aleatorios para comprobar si hay errores ocultos y si hay un miembro NameBlock entrar recursivamente a chequearlo. Lo más complicado es la generación de argumentos aleatorios de cualquier tipo, tanto del sistema (Real, Set, ...) como de usuario (Struct, Class), pero tampoco es algo inviable. Este tipo de diagnosis daría una robustez al código que de intentar hacerse a mano mediante chequeo exhaustivo de todos los métodos sería una tarea casi infinita.
comment:4 Changed 14 years ago by
Resolution: | → remind |
---|---|
Status: | new → closed |
comment:5 Changed 14 years ago by
Una alternativa que he encontrado práctica, y quizá algo más elegante, para evitar tener que definir cada nameblock local como una clase de una instancia e instanciarla es crear una clase que represente a los nameblocks locales al paquete:
Class @LocalNameBlock { Static Text _.autodoc.description = "Clase auxiliar que permite a los nameblocks creados con ella " "acceder a las funciones locales definidas dentro del paquete." };
y crear los nameblocks como:
@LocalNameBlock <nombre> = [[ ... ]];
en lugar de:
NameBlock <nombre> = [[ ... ]];
Yo en su día intenté programar el acceso a todos los antecesores pero eso es bastante problemático, se crea un lío de ámbitos tremendo. Yo creo que lo más natural es usar el prefijo del NameBlock dueño de lo que se quiere acceder, al menos es lo que suelo hacer yo y me funciona siempre.
¿Ves algún problema a esta solución?