close Warning: Can't synchronize with repository "(default)" (/var/svn/tolp does not appear to be a Subversion repository.). Look in the Trac log for more information.

Opened 15 years ago

Closed 14 years ago

Last modified 14 years ago

#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 Víctor de Buen Remiro

Milestone: OOP Implementation

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.

NameBlock N0A = [[
  Real F1(Real void) { 1 };
  NameBlock N2 = [[
    Real F2(Real void) {  N0A::F1(?) }
  ]]
]];

Real  N0A::N2::F2(?);

¿Ves algún problema a esta solución?

comment:2 Changed 15 years ago by Pedro Gea

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 Víctor de Buen Remiro

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 Víctor de Buen Remiro

Resolution: remind
Status: newclosed

comment:5 Changed 14 years ago by Pedro Gea

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> = [[ ... ]];
Note: See TracTickets for help on using tickets.