buenas, imaginemos que voy a implementar un modulo que expone una api que tiene multiples implementaciones para distintos casos y uno puede hacer una nueva implementacion la idea es que el usuario de la api siempre use la misma api y tenga que hacer el minimo trabajo para ponerlo a andar y que no tenga que hacer grandes cambios si cambia de implementacion ejemplo, una libreria para manipular xml o html tenemos la api externa y una implementacion para xml y otra para html (y posiblemente otras para otros tipos de datos) como implementarian esto? PD: me quiero mantener alejado de parametrized modules
Este es todavía un problema en Erlang. Yo no encontré todavía una forma "limpia" de hacer algo que los lenguajes orientados a objectos resuelven con mucha facilidad combinando herencia y polimorfismo. 1) La primer opción que se me ocurre es la que ya descartaste (módulos parametrizados). 2) Crear un behaviour con la interfaz que vas a querer soportar en cada uno de los módulos y después implementar los módulos. En la aplicación tendrías que guardar el nombre del módulo en una variable e invocar a las funciones usando la variable (ej. Module:func()). 3) Armar un módulo que actúe como wrapper e invoque al módulo que corresponda según lo que recibe. 4) Si los módulos tienen mucho código y datos en común podrías usar la herencia de módulos (-extends()), pero ésto es aún menos usado que los módulos parametrizados. Podés ver una presentación de ésto en: http://www.erlang.org/euc/07/papers/1700Carlsson.pdf Yo he usado las 3 primeras opciones en distintos casos. La forma más simple es con módulos parametrizados, pero casi nadie les tiene mucho aprecio. Lo raro es que los que más lo usan (mochiweb y misultin para encapsular a los requests HTTP) no veo que lo necesiten tanto. Juanjo 2011/7/8 Mariano Guerra <luismarianoguerra@gmail.com> > buenas, > > imaginemos que voy a implementar un modulo que expone una api que > tiene multiples implementaciones para distintos casos y uno puede > hacer una nueva implementacion > > la idea es que el usuario de la api siempre use la misma api y tenga > que hacer el minimo trabajo para ponerlo a andar y que no tenga que > hacer grandes cambios si cambia de implementacion > > ejemplo, una libreria para manipular xml o html > > tenemos la api externa y una implementacion para xml y otra para html > (y posiblemente otras para otros tipos de datos) > > como implementarian esto? > > PD: me quiero mantener alejado de parametrized modules >
2011/7/8 Juan Jose Comellas <juanjo@comellas.org>: > 4) Si los módulos tienen mucho código y datos en común podrías usar la > herencia de módulos (-extends()), pero ésto es aún menos usado que los > módulos parametrizados. Podés ver una presentación de ésto en: > http://www.erlang.org/euc/07/papers/1700Carlsson.pdf > Esta no la tenía... a leer! :) Saludos, Nahuel Greco.
El día 8 de julio de 2011 11:16, Nahuel Greco <ngreco@gmail.com> escribió: > 2011/7/8 Juan Jose Comellas <juanjo@comellas.org>: >> 4) Si los módulos tienen mucho código y datos en común podrías usar la >> herencia de módulos (-extends()), pero ésto es aún menos usado que los >> módulos parametrizados. Podés ver una presentación de ésto en: >> http://www.erlang.org/euc/07/papers/1700Carlsson.pdf >> > > Esta no la tenía... a leer! :) Este también tiene mucha pinta: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.58.79&rep=rep1&type=pdf > > Saludos, > Nahuel Greco. > -- Ale.
> Yo he usado las 3 primeras opciones en distintos casos. La forma más simple > es con módulos parametrizados, pero casi nadie les tiene mucho aprecio. Lo > raro es que los que más lo usan (mochiweb y misultin para encapsular a los > requests HTTP) no veo que lo necesiten tanto. En chicago boss los usan http://www.evanmiller.org/chicago-boss-guide.html para crear los modelos de datos. Cuando los vi ahí me parecieron muy interesantes y útiles. Saludos, -- Ale.
Tendrías un modulo A que usa una API B que puede estar implementada en C o D, creo que se reduce a: 1- Usar parametrized modules (por qué no querés?). 2- Declarar en B un behaviour a implementarse por C/D y hacer un switcheo de files a C o D en el build process. 3- Mantener el estado en A de que estás usando C/D 3.1- Si hay código en B que usa a C/D (ej, si quisieras hacer algo como http://en.wikipedia.org/wiki/Template_method_pattern ), declarar un behaviour en B a ser implementado por C/D y en A mantener un estado (que podría ser un proceso lanzado por B, o en su forma mínima un atomo 'c', 'd') que pasarías al llamar a las funcs de B, que a su vez usan las de C/D. 3.2- Si B existe solo para definir la API, definirla como behaviour y que C/D la implementen. Luego en A usar C/D directamente a través de una constante ?MODCorD:fun1(). 4- (No es un idioma común) Retornar desde B un record con closures de C/D y desde A usar ese record. Saludos, Nahuel Greco. 2011/7/8 Mariano Guerra <luismarianoguerra@gmail.com>: > buenas, > > imaginemos que voy a implementar un modulo que expone una api que > tiene multiples implementaciones para distintos casos y uno puede > hacer una nueva implementacion > > la idea es que el usuario de la api siempre use la misma api y tenga > que hacer el minimo trabajo para ponerlo a andar y que no tenga que > hacer grandes cambios si cambia de implementacion > > ejemplo, una libreria para manipular xml o html > > tenemos la api externa y una implementacion para xml y otra para html > (y posiblemente otras para otros tipos de datos) > > como implementarian esto? > > PD: me quiero mantener alejado de parametrized modules >
2011/7/8 Nahuel Greco <ngreco@gmail.com>: > Tendrías un modulo A que usa una API B que puede estar implementada en > C o D, creo que se reduce a: > > 1- Usar parametrized modules (por qué no querés?). me parece un hack medio fiero :S > 2- Declarar en B un behaviour a implementarse por C/D y hacer un > switcheo de files a C o D en el build process. el tema es que todas las implementaciones tendrian que estar disponibles en tiempo de ejecucion, me parece que lo de behavours es la bocha, pense que eran algo propio de otp, lo mio seria un modulo con una api comun y corriente > 3- Mantener el estado en A de que estás usando C/D > 3.1- Si hay código en B que usa a C/D (ej, si quisieras hacer algo > como http://en.wikipedia.org/wiki/Template_method_pattern ), declarar > un behaviour en B a ser implementado por C/D y en A mantener un estado > (que podría ser un proceso lanzado por B, o en su forma mínima un > atomo 'c', 'd') que pasarías al llamar a las funcs de B, que a su vez > usan las de C/D. pero de esta forma las implementaciones correrian en un proceso aparte? seria un cuello de botella si por ejemplo multiples procesos quieren correr funciones del modulo. > 3.2- Si B existe solo para definir la API, definirla como behaviour > y que C/D la implementen. Luego en A usar C/D directamente a través de > una constante ?MODCorD:fun1(). como dije arriba en tiempod e ejecucion se pueden usar los dos modulos a la vez > 4- (No es un idioma común) Retornar desde B un record con closures de > C/D y desde A usar ese record. estaba pensando en algo como un closure pero me parecio medio hackoso. *** aclaro por que me parece que no fui claro. quiero algo asi como una interfaz de java/go o abstract base class de python, digamos, A expresa la api y los otros la implementan, pero quiero enforcement en tiempo de compilacion si es posible. otra cosa es que mi modulo/api va a ser un modulo, osea no va a ser un gen_server ni nada por el estilo y es probable que algunas llamadas quieran ser lanzadas en otro proceso por lo que levantar un unico proceso es un bottleneck y que el cliente tenga que levantar un proceso me parece un poco un hack. ahora me pongo a mirar behaviours disclaimer (aunque creo que se nota): si bien he programado en erlang bastante nunca use toda la parte otp asi que por ahi estoy preguntando una boludes :P
2011/7/8 Mariano Guerra <luismarianoguerra@gmail.com>: > 2011/7/8 Nahuel Greco <ngreco@gmail.com>: > >> 2- Declarar en B un behaviour a implementarse por C/D y hacer un >> switcheo de files a C o D en el build process. > > el tema es que todas las implementaciones tendrian que estar > disponibles en tiempo de ejecucion, me parece que lo de behavours es > la bocha, pense que eran algo propio de otp, lo mio seria un modulo > con una api comun y corriente > El tema de Behaviours es un feature del lenguaje, no de OTP. El problema es que siempre se explica relacionado a OTP y por eso confunde, pero es muy simple. En un modulo B definís algo similar a una interface en Java: -module(b) -export([behaviour_info/1, start:/1]) behaviour_info(callbacks) -> [{myfun,1}]; behaviour_info(_Other) -> undefined. Lo que declara que si otro módulo quiere implementar el behaviour b, va a tener que implementar y exportar myfun/1. Luego en el modulo A hacés: -module(a) -behaviour(b) -export([myfun/1]) myfun -> %% implementacion de myfun Y lo que hace el compilador cuando compila A es simplemente tirar warnings si le falta definir myfun u otra funcion declarada en el behaviour. Nada más, that's all. >> 3- Mantener el estado en A de que estás usando C/D >> 3.1- Si hay código en B que usa a C/D (ej, si quisieras hacer algo >> como http://en.wikipedia.org/wiki/Template_method_pattern ), declarar >> un behaviour en B a ser implementado por C/D y en A mantener un estado >> (que podría ser un proceso lanzado por B, o en su forma mínima un >> atomo 'c', 'd') que pasarías al llamar a las funcs de B, que a su vez >> usan las de C/D. > > pero de esta forma las implementaciones correrian en un proceso > aparte? seria un cuello de botella si por ejemplo multiples procesos > quieren correr funciones del modulo. > Desgloso las dos posibilidades: 1- desde A harías b:fun(c) (sin procesos extra). 2- desde A harías Pid = b:start(c), lo que te devolvería un proceso que incluye el estado de que lo inicializaste con c, y luego usarías b:funx(Pid).. Es similar a usar gen_server:call(atom... o gen_server:call(Pid.. >> 3.2- Si B existe solo para definir la API, definirla como behaviour >> y que C/D la implementen. Luego en A usar C/D directamente a través de >> una constante ?MODCorD:fun1(). > > como dije arriba en tiempod e ejecucion se pueden usar los dos modulos a la vez > Bueno, en ese caso para esto no sería una constante sino una variable Module = (c / d). > estaba pensando en algo como un closure pero me parecio medio hackoso. > No es un idioma usual. Saludos, Nahuel Greco.
Las behaviours son bastante fáciles de usar. Son básicamente módulos que en
la invocación a la función behaviour_info(callbacks) responden con una lista
de tuplas con el nombre y la aridad de las funciones de la interfaz que
querés proveer.
Ejemplo:
-module(pbx_fsm).
-export([behaviour_info/1]).
-spec behaviour_info(callbacks | any()) -> [{FunctionName :: atom(), Arity
:: non_neg_integer()}] | undefined.
behaviour_info(callbacks) ->
[{dialplan, 3},
{on_event, 2},
{execute, 2},
{execute, 3}];
behaviour_info(_Other) ->
undefined.
En este ejemplo todos los módulos que implementen la interfaz del behaviour
van a tener que definir las funciones: dialplan/3, on_event/2, execute/2 y
execute/3 de la siguiente manera:
-module(pbx_fsm_conference).
-behaviour(pbx_fsm).
-export([dialplan/3, on_event/2, execute/2, execute/3]).
[... implementación de las funciones ...]
Saludos,
Juanjo
2011/7/8 Mariano Guerra <luismarianoguerra@gmail.com>
> 2011/7/8 Nahuel Greco <ngreco@gmail.com>:
> > Tendrías un modulo A que usa una API B que puede estar implementada en
> > C o D, creo que se reduce a:
> >
> > 1- Usar parametrized modules (por qué no querés?).
>
> me parece un hack medio fiero :S
>
> > 2- Declarar en B un behaviour a implementarse por C/D y hacer un
> > switcheo de files a C o D en el build process.
>
> el tema es que todas las implementaciones tendrian que estar
> disponibles en tiempo de ejecucion, me parece que lo de behavours es
> la bocha, pense que eran algo propio de otp, lo mio seria un modulo
> con una api comun y corriente
>
> > 3- Mantener el estado en A de que estás usando C/D
> > 3.1- Si hay código en B que usa a C/D (ej, si quisieras hacer algo
> > como http://en.wikipedia.org/wiki/Template_method_pattern ), declarar
> > un behaviour en B a ser implementado por C/D y en A mantener un estado
> > (que podría ser un proceso lanzado por B, o en su forma mínima un
> > atomo 'c', 'd') que pasarías al llamar a las funcs de B, que a su vez
> > usan las de C/D.
>
> pero de esta forma las implementaciones correrian en un proceso
> aparte? seria un cuello de botella si por ejemplo multiples procesos
> quieren correr funciones del modulo.
>
> > 3.2- Si B existe solo para definir la API, definirla como behaviour
> > y que C/D la implementen. Luego en A usar C/D directamente a través de
> > una constante ?MODCorD:fun1().
>
> como dije arriba en tiempod e ejecucion se pueden usar los dos modulos a la
> vez
>
> > 4- (No es un idioma común) Retornar desde B un record con closures de
> > C/D y desde A usar ese record.
>
> estaba pensando en algo como un closure pero me parecio medio hackoso.
>
> ***
>
> aclaro por que me parece que no fui claro.
>
> quiero algo asi como una interfaz de java/go o abstract base class de
> python, digamos, A expresa la api y los otros la implementan, pero
> quiero enforcement en tiempo de compilacion si es posible.
>
> otra cosa es que mi modulo/api va a ser un modulo, osea no va a ser un
> gen_server ni nada por el estilo y es probable que algunas llamadas
> quieran ser lanzadas en otro proceso por lo que levantar un unico
> proceso es un bottleneck y que el cliente tenga que levantar un
> proceso me parece un poco un hack.
>
> ahora me pongo a mirar behaviours
>
> disclaimer (aunque creo que se nota): si bien he programado en erlang
> bastante nunca use toda la parte otp asi que por ahi estoy preguntando
> una boludes :P
>